[bzoj3172][TJOI2013]单词

来源:互联网 发布:小白鞋推荐 知乎 编辑:程序博客网 时间:2024/05/16 04:09

题目大意

给定许多个单词,求每个单词在所有单词中出现的次数。

SAM

显然可以用SAM做。
一种是在单词与单词间加入字符{,第二种是trie上建sam,最简单当然是建广义后缀自动机。
建后缀树做法可以看用SAM建广义后缀树

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;const int maxn=2500000+10;int pre[maxn],step[maxn],size[maxn],g[maxn][27],id[maxn];char s[maxn],ch;int i,j,k,l,t,n,m,tot,top,last;char get(){    char ch=getchar();    while (ch<'a'||ch>'z') ch=getchar();    return ch;}void insert(char ch){    int np=++tot;    size[np]=1;    step[np]=step[last]+1;    int p=last;    while (p&&g[p][ch-'a']==0){        g[p][ch-'a']=np;        p=pre[p];    }    if (!p) pre[np]=1;    else{        int q=g[p][ch-'a'];        if (step[q]==step[p]+1) pre[np]=q;        else{            int nq=++tot;            step[nq]=step[p]+1;            pre[nq]=pre[q];            pre[q]=nq;            int i;            fo(i,0,26) g[nq][i]=g[q][i];            pre[np]=nq;            while (p&&g[p][ch-'a']==q){                g[p][ch-'a']=nq;                p=pre[p];            }        }    }    last=np;}bool cmp(int a,int b){    if (step[a]<step[b]) return 1;    else if (step[a]==step[b]&&a<b) return 1;    else return 0;}int main(){    //freopen("word4.in","r",stdin);freopen("3127.out","w",stdout);    scanf("%d",&n);    last=tot=1;    fo(i,1,n){        s[++top]=get();        insert(s[top]);        while (1){            ch=getchar();            if (ch<'a'||ch>'z') break;            s[++top]=ch;            insert(s[top]);        }        s[++top]='a'+26;        insert(s[top]);    }    fo(i,1,tot) id[i]=i;    sort(id+1,id+tot+1,cmp);    fd(i,tot,1)        if (pre[id[i]]>1) size[pre[id[i]]]+=size[id[i]];    j=1;    fo(i,1,top){        if (s[i]=='a'+26){            printf("%d\n",size[j]);            j=1;        }        else j=g[j][s[i]-'a'];    }}
0 0