[BZOJ3172][Tjoi2013]单词

原题地址

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

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

一个状态的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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值