[BZOJ3172][Tjoi2013]单词

来源:互联网 发布:如何制作风险矩阵 编辑:程序博客网 时间:2024/05/15 17:13

原题地址

我们把所有单词”拼”起来成为一个串,两个单词之间用分隔符隔开(我的代码里是’{‘),那么要求的就是每个单词在这个拼起来的串中的出现次数。

那么这个就是后缀自动机统计子串出现次数的经典问题。

一个状态的Right集大小就是以它为起点,以一个终态为终点的路径条数(如果这个点本身就是终态,那么这一个点也算一条合法路径),那么统计这个可以用拓扑排序后DP解决(从FHQ博客上看的),我用的是记忆化搜索,感觉好写一点…

貌似还可以用Parent树来搞,不过我太弱了不会写OLZ…

写的时候忘记更新nq节点的nq->l了,导致出错,下次一定要注意.

AC code:

#include <cstdio>#include <cstring>const int K=27;const int N=210;const int L=2100010;int  n,cnt,ls;int  len[N],head[N];bool vis[L];char s[L],t[L];struct nod{    int l,w,num;    nod *pr,*ch[K];}pool[L];struct SAM{    nod *root,*last;    SAM(){        root=last=&pool[0];    }    void extend(int x){        nod *p=last,*np=&pool[++cnt];        for(last=np,np->num=cnt,np->l=p->l+1;p&&(!p->ch[x]);p->ch[x]=np,p=p->pr) ;        if(!p) np->pr=root;        else{            nod *q=p->ch[x];            if(p->l+1==q->l) np->pr=q;            else{                nod *nq=&pool[++cnt];                *nq=*q;nq->l=p->l+1;nq->num=cnt;q->pr=np->pr=nq;                for(;p&&p->ch[x]==q;p->ch[x]=nq,p=p->pr) ;            }        }    }    void dfs(nod *p){        vis[p->num]=1;        for(int i=0;i<K;i++){            if(!p->ch[i]) continue;            if(!vis[p->ch[i]->num]) dfs(p->ch[i]);            p->w+=p->ch[i]->w;        }    }    void build(){        for(nod *i=last;i!=root;i=i->pr) i->w=1;        dfs(root);    }    void match(int L,int R){        nod *p=root;        for(int i=L;i<=R;p=p->ch[s[i++]-'a']) ;        printf("%d\n",p->w);    }}AM;int main(){    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%s",t);        len[i]=strlen(t);        strcat(s,t);        int l=strlen(s);        s[l]='{';        s[l+1]='\0';        head[i+1]=l+1;    }    ls=strlen(s);    for(int i=0;i<ls;i++) AM.extend(s[i]-'a');    AM.build();    for(int i=1;i<=n;i++) AM.match(head[i],head[i]+len[i]-1);    return 0;}
0 0
原创粉丝点击