bzoj3172: [Tjoi2013]单词

来源:互联网 发布:java生成二维数组 编辑:程序博客网 时间:2024/05/29 15:51

Description

某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。

Input

第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6

Output

输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。

Sample Input

3
a
aa
aaa

Sample Output

6
3
1
题解:
先将所有字符串拼起来,然后求一遍后缀数组,找到第以i个串开头的后缀的位置,求出向左走保证height[x]>=len[i]的最小的x,以及向右走height[y]>=len[i]的最大的y,那么答案就是y-x+2;
向左扩展或向右扩展在扩展的过程中,min(height[x])肯定单调不增,所以可以二分答案,rmq预处理区间最小值,时间复杂度O(nlogn)
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn=1000000+210;char A[maxn];int sa[maxn],rk[maxn],tmp[maxn],c[maxn];int n;int minn[maxn][22];int sum[maxn];int len[maxn];int height[maxn];int lg[maxn];int tot=0;char B[maxn];inline void get_sa(){    int *rnk=rk,*tp=tmp;    n=strlen(A+1);    int m=300;    for(int i=1;i<=n;i++)        tp[i]=A[i],rnk[i]=A[i];    for(int i=0;i<=m;i++)        c[i]=0;    for(int i=1;i<=n;i++)        c[tp[i]]++;    for(int i=1;i<=m;i++)        c[i]+=c[i-1];    for(int i=n;i>=1;i--)        sa[c[tp[i]]--]=i;    for(int j=1;j<=n;j<<=1){        int p=0;        for(int i=n-j+1;i<=n;i++)            tp[++p]=i;        for(int i=1;i<=n;i++)            if(sa[i]>j)                tp[++p]=sa[i]-j;        for(int i=0;i<=m;i++)            c[i]=0;        for(int i=1;i<=n;i++)            c[rnk[tp[i]]]++;        for(int i=1;i<=m;i++)            c[i]+=c[i-1];        for(int i=n;i>=1;i--)            sa[c[rnk[tp[i]]]--]=tp[i];        swap(rnk,tp);        rnk[sa[1]]=1;        p=1;        for(int i=2;i<=n;i++){            int O1=sa[i]+j>n?-1:tp[sa[i]+j];            int O2=sa[i-1]+j>n?-1:tp[sa[i-1]+j];            rnk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&O1==O2)?p:++p;        }        m=p;        if(m>=n)            break;    }}inline void get_height(){    for(int i=1;i<=tot;i++)        rk[sa[i]]=i;    int h=0;    for(int i=1;i<=tot;i++){        --h=h<0?0:h;        int u=sa[rk[i]-1];        while(A[u+h]==A[i+h])            h++;        height[rk[i]]=h;    }}inline void rmq_pre(){    for(int i=1;i<=tot;i++){        int u=1,cnt=0;        while(u<=i)            u<<=1,cnt++;        lg[i]=cnt-1;    }    for(int i=1;i<=tot;i++)        minn[i][0]=height[i];    for(int j=1;j<=30;j++)        for(int i=1;i<=tot;i++){            if(i+(1<<j)-1>tot)                break;            minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);        }}inline int get_min(int l,int r){    if(l>r)        return 0x3f3f3f3f;    int u=lg[r-l+1];    return min(minn[l][u],minn[r-(1<<u)+1][u]);}inline int get_l(int x,int length){    int l=1,r=x;    while(l+1<r){        int mid=(l+r)>>1;        int u=get_min(mid+1,x);        if(u>=length)            r=mid;        else l=mid;    }    if(get_min(l+1,x)>=length)        return l;    return r;}inline int get_r(int x,int length){    int l=x,r=tot;    while(l+1<r){        int mid=(l+r)>>1;        int u=get_min(x+1,mid);        if(u>=length)            l=mid;        else r=mid;    }    if(get_min(x+1,r)>=length)        return r;    return l;}inline void work(int i){    int t=rk[sum[i-1]+1];    int l=get_l(t,len[i]),r=get_r(t,len[i]);    //printf("%d %d\n",l,r);    printf("%d\n",r-l+1);}int main(){    //freopen("a.in","r",stdin);    //freopen("a.out","w",stdout);    int n;    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%s",B+1);        len[i]=strlen(B+1);        for(int j=1;j<=len[i];j++)            A[++tot]=B[j];        if(i!=n)            A[++tot]='z'+1;        sum[i]=sum[i-1]+len[i]+(i!=n);    }    get_sa();    get_height();    rmq_pre();    for(int i=1;i<=n;i++)        work(i);    //for(int i=1;i<=tot;i++)    //  printf("%d ",sa[i]);return 0;}




阅读全文
0 0