zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
    
  • 相关阅读:
    随机-byte编码
    dataframe骚操作,待续
    oracle中的rowid
    java提高篇-----理解java的三大特性之继承
    staruml使用教程
    黑马程序员:HTML习题1
    Cocos2d-x-->CCSprite 动画
    地址栏传参中文乱码详解
    Qt学习第二天
    Lync 2010 升级到2013 之部署2013前端服务器!
  • 原文地址:https://www.cnblogs.com/tarjan/p/7455013.html
Copyright © 2011-2022 走看看