• codefores 204E. Little Elephant and Strings(后缀数组,RMQ求lcp,二分,主席树)


    题目链接

    E. Little Elephant and Strings
    time limit per test3 seconds
    memory limit per test256 megabytes
    inputstandard input
    outputstandard output
    The Little Elephant loves strings very much.

    He has an array a from n strings, consisting of lowercase English letters. Let's number the elements of the array from 1 to n, then let's denote the element number i as ai. For each string ai (1 ≤ i ≤ n) the Little Elephant wants to find the number of pairs of integers l and r (1 ≤ l ≤ r ≤ |ai|) such that substring ai[l... r] is a substring to at least k strings from array a (including the i-th string).

    Help the Little Elephant solve this problem.

    If you are not familiar with the basic notation in string problems, you can find the corresponding definitions in the notes.

    Input
    The first line contains two space-separated integers — n and k (1 ≤ n, k ≤ 105). Next n lines contain array a. The i-th line contains a non-empty string ai, consisting of lowercase English letter. The total length of all strings ai does not exceed 105.

    Output
    On a single line print n space-separated integers — the i-th number is the answer for string ai.

    Please, do not use the %lld specifier to read or write 64-bit integers in С++. It is preferred to use the cin, cout streams or the %I64d specifier.

    Examples
    input
    3 1
    abc
    a
    ab
    output
    6 1 3
    input
    7 4
    rubik
    furik
    abab
    baba
    aaabbbababa
    abababababa
    zero
    output
    1 0 9 9 21 30 0
    Note
    Let's assume that you are given string a = a1a2... a|a|, then let's denote the string's length as |a| and the string's i-th character as ai.

    A substring a[l... r] (1 ≤ l ≤ r ≤ |a|) of string a is string alal + 1... ar.

    String a is a substring of string b, if there exists such pair of integers l and r (1 ≤ l ≤ r ≤ |b|), that b[l... r] = a.

    题意:给出 (n) 个字符串和 (k) ,对于每个字符串输出其子串的个数,使得这些子串在总共的 (n) 个字符串中至少不同的 (k) 个中出现过。

    题解:这一题搜题解都看不懂,然后自己想了一下,先将所有子串拼起来(这个不用说),延续上一题的思想,将 (sa) 看成一段处理的数组,对于每个 (sa[i]) ,二分其能作为至少不同 (k) 个串的长度 (l),然后进行判定,判定时向前向后在 (sa) 数组中找 (lcp)(k) 时能够延伸的最远边界 (l,r) ,然后查询这段区间 ([l,r]) 有多少个不同的串(这就要用到主席树了)。最后将得到的值加入 (sa[i]) 对应的串。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<set>
    using namespace std;
    #define rep(i,a,n) for (int i=a;i<n;i++)
    #define per(i,a,n) for (int i=n-1;i>=a;i--)
    #define pb push_back
    #define fi first
    #define se second
    #define dbg(...) cerr<<"["<<#__VA_ARGS__":"<<(__VA_ARGS__)<<"]"<<endl;
    typedef vector<int> VI;
    typedef long long ll;
    typedef pair<int,int> PII;
    const int maxn=2e5+10;
    /*
     *suffix array
     *倍增算法  O(n*logn)
     *待排序数组长度为n,放在0~n-1中,在最后面补一个0
     *build_sa( ,n+1,m+1); //注意是n+1,m是s数组中的最大值;
     *getHeight(,n);
     *例如:
     
     *n   = 8;
     *num[]   = { 1, 1, 2, 1, 1, 1, 1, 2, $ };注意num最后一位为0,其他大于0
     *rank[]  = { 4, 6, 8, 1, 2, 3, 5, 7, 0 };rank[0~n-1]为有效值,rank[n]必定为0无效值
     *sa[]    = { 8, 3, 4, 5, 0, 6, 1, 7, 2 };sa[1~n]为有效值,sa[0]必定为n是无效值
     *height[]= { 0, 0, 3, 2, 3, 1, 2, 0, 1 };height[2~n]为有效值
     *
     */
    
    int sa[maxn];//SA数组,表示将S的n个后缀从小到大排序后把排好序的
    //的后缀的开头位置顺次放入SA中
    int t1[maxn],t2[maxn],c[maxn];//求SA数组需要的中间变量,不需要赋值
    int rk[maxn],height[maxn];
    //待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m,
    //除s[n-1]外的所有s[i]都大于0,r[n-1]=0
    //函数结束以后结果放在sa数组中
    void build_sa(int s[],int n,int m)
    {
        int i,j,p,*x=t1,*y=t2;
        //第一轮基数排序,如果s的最大值很大,可改为快速排序
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[i]=s[i]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
        for(j=1;j<=n;j<<=1)
        {
            p=0;
            //直接利用sa数组排序第二关键字
            for(i=n-j;i<n;i++)y[p++]=i;//后面的j个数第二关键字为空的最小
            for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
            //这样数组y保存的就是按照第二关键字排序的结果
            //基数排序第一关键字
            for(i=0;i<m;i++)c[i]=0;
            for(i=0;i<n;i++)c[x[y[i]]]++;
            for(i=1;i<m;i++)c[i]+=c[i-1];
            for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
            //根据sa和x数组计算新的x数组
            swap(x,y);
            p=1;x[sa[0]]=0;
            for(i=1;i<n;i++)
                x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
            if(p>=n)break;
            m=p;//下次基数排序的最大值
        }
    }
    void getHeight(int s[],int n)
    {
        int i,j,k=0;
        for(i=0;i<=n;i++) rk[sa[i]]=i;
        for(i=0;i<n;i++)
        {
            if(k)k--;
            j=sa[rk[i]-1];
            while(s[i+k]==s[j+k])k++;
            height[rk[i]]=k;
        }
    }
    
    
    int a[maxn],bl[maxn];
    char s[maxn];
    struct node{int l,r,sum;}T[maxn*40];
    
    int tot=0;
    int root[maxn];
    void update(int l,int r,int &x,int y,int pos,int val)
    {
        T[++tot]=T[y],T[tot].sum+=val;
        x=tot;
        if(l==r) return;
        int m=(l+r)/2;
        if(pos<=m) update(l,m,T[x].l,T[y].l,pos,val);
        else update(m+1,r,T[x].r,T[y].r,pos,val);
    }
    int query(int l,int r,int root,int k)
    {
        if(l==r) return T[root].sum;
        int m=(l+r)/2;
        if(m<k) return T[T[root].l].sum+query(m+1,r,T[root].r,k);
        else return query(l,m,T[root].l,k);
    }
    
    
    int d[maxn][19];
    int cnt;
    void init_RMQ(int n)
    {
        for(int i=1;i<=n;i++) d[i][0]=height[i];
        for(int k=1;(1<<k)<=n;k++)
            for(int i=0;i+(1<<k)<=n;i++)
                d[i][k]=min(d[i][k-1],d[i+(1<<(k-1))][k-1]);
    }
    int query(int l,int r)
    {
        int k=0;
        while(1<<(k+1)<=r-l+1) k++;
        return min(d[l][k],d[r-(1<<k)+1][k]);
    }
    int lcp(int l,int r) //后缀l和后缀r
    {
        if(l==r) return cnt-sa[l];
        if(l>r) swap(l,r);
        if(l+1==r) return height[r];
        else return query(l+1,r);
    }
    
    int getl(int x,int k)
    {
        int l=1,r=x;
        int ans=0;
        while(l<=r)
        {
            int m=(l+r)/2;
            if(lcp(m,x)>=k)
            {
                ans=m;
                r=m-1;
            }
            else l=m+1;
        }
        return ans;
    }
    
    int getr(int x,int k)
    {
        int l=x,r=cnt;
        int ans=0;
        while(l<=r)
        {
            int m=(l+r)/2;
            if(lcp(m,x)>=k)
            {
                ans=m;
                l=m+1;
            }
            else r=m-1;
        }
        return ans;
    }
    ll res[maxn];
    int mp[maxn];
    int main()
    {
        int n,k;
        scanf("%d%d",&n,&k);
        cnt=0;
        int mx='z'-'a'+2;
        rep(i,1,n+1)
        {
            scanf("%s",s);
            int l=(int)strlen(s);
            if(k==1) printf("%lld ",1ll*l*(l+1)/2);
            rep(j,0,l) a[cnt]=s[j]-'a'+1,bl[cnt++]=i;
            if(i<n) a[cnt]=mx++,bl[cnt++]=n+1;
        }
        if(k==1)
        {
            return 0*puts("");
        }
        a[cnt]=0;bl[cnt]=n+1;
        build_sa(a,cnt+1,mx+2);
        getHeight(a,cnt);
        init_RMQ(cnt);
        per(i,1,cnt+1)
        {
            int t=bl[sa[i]];
            if(!mp[t])
            {
                update(1,cnt,root[i],root[i+1],i,1);
            }
            else
            {
                update(1,cnt,root[i],root[i+1],mp[t],-1);
                update(1,cnt,root[i],root[i],i,1);
            }
            mp[t]=i;
        }
        rep(i,1,cnt+1)
        {
            if(bl[sa[i]]==n+1) continue;
            int l=0,r=max(height[i],height[i+1]);
            int ans=0;
            while(l<=r)
            {
                int m=(l+r)/2;
                int fl=getl(i,m),fr=getr(i,m);
                int num=query(1,cnt,root[fl],fr);
                if(num>=k)
                {
                    ans=m;
                    l=m+1;
                }
                else r=m-1;
            }
            res[bl[sa[i]]]+=1ll*ans;
        }
        rep(i,1,n+1) printf("%lld ",res[i]);
        puts("");
        return 0;
    }
    
    
    
  • 相关阅读:
    在intent-filter中的配置
    利用asynchttpclient开源项目来把数据提交给服务器
    URL的应用
    ScrollView在布局中的作用
    android中传统的创建数据库
    for (Sms sms : smsLists){}
    Android中对文件的读写进行操作
    android中的5大布局
    android复制包需要修改的几个地方
    【Unity Tips】备忘录(扫盲篇)
  • 原文地址:https://www.cnblogs.com/tarjan/p/7455013.html
Copyright © 2020-2023  润新知