spoj 8222 Substrings(NSUBSTR),后缀自动机

来源:互联网 发布:民间借贷知乎 编辑:程序博客网 时间:2024/06/10 04:32

spoj 8222  Substrings

f[i]指长度为i的串出现次数的最大值。这里的不同出现指,可以有重复串,只要起始位置不同就视为不同的出现。

求f[1]..f[lenth]。

 

怎么求可重复的一个串出现了多少次。

LCS那篇里提到了,通过fa指针回退,会回到一个状态,这个状态的接受串与当前状态的接受串后缀是相同的。

那么,设t[i].fa=j,那么i状态的接受串也必定出现在j状态的接受串的后缀中。又注意到ij的接受串的结束位置是不同的,这样,i状态至少出现了两次。

 

通过这样的规律,通过fa边的拓扑序dp统计答案即可。

还有一点,一个状态的接受串有可能有多种。如aab,b有可能被同一个状态接受。

如果aab出现了n次,那么b也可能出现n次。而aab的任意子串如aa、ab也出现n次。

这样只需要从最长到最短更新dp的值,就可以不漏掉任何情况。

 

WA了很久,一直卡在一个地方。翻了很多题解才有所进展。

做初始化时,dp[i]不能全部置为1,而是当i不是分裂的节点(就是构造函数中的nq)这样的节点才置1。

很容易想到,一个节点分裂了,如果都置1,父节点会重复计算。

但是该分裂节点如何保证正确计数?因为构造中,新分裂的节点nq会成为q的父亲,因此q的计数也会加到nq上,就保证了计数的正确。

 

huyuncong这位大牛写的后缀自动机性质很棒。网址如下:

http://blog.csdn.net/huyuncong/article/details/7583214

引用并补充大牛博客的总结:

一个串的子串有多少之类的问题,或是询问子串/后缀的问题,就用子边转移(自动机性质)。

而计算一个串重复出现次数(right集合的问题),回退到最长匹配状态(LCS问题),就用父边转移(后缀树性质)

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define Maxn 250009int root,last;//samint tots;struct sam_node{    int fa,son[26];    int len;    void init(int _len){len=_len;fa=-1;memset(son,-1,sizeof(son));}}t[Maxn*2];//length*2void sam_init(){    tots=0;    root=last=0;    t[tots].init(0);}void extend(int w){    int p=last;    int np=++tots;t[tots].init(t[p].len+1);    int q,nq;    while(p!=-1&&t[p].son[w]==-1){t[p].son[w]=np;p=t[p].fa;}    if (p==-1) t[np].fa=root;    else{        q=t[p].son[w];        if (t[p].len+1==t[q].len){t[np].fa=q;}        else{            nq=++tots;t[nq].init(0);            t[nq]=t[q];            t[nq].len=t[p].len+1;            t[q].fa=nq;t[np].fa=nq;            while(p!=-1&&t[p].son[w]==q){t[p].son[w]=nq;p=t[p].fa;}        }    }    last=np;}int w[Maxn],r[Maxn*2],l;void topsort(){    int i;    for(i=0;i<=l;++i)   w[i]=0;    for(i=1;i<=tots;++i)w[t[i].len]++;    for(i=1;i<=l;++i)   w[i]+=w[i-1];    for(i=tots;i>=1;--i)r[w[t[i].len]--]=i;    r[0]=0;}int dp[Maxn*2],f[Maxn];char s[Maxn];int main(){    int i,p;    scanf("%s",s);    l=strlen(s);    sam_init();    for(i=0;i<l;++i){        extend(s[i]-'a');    }    topsort();        //for(i=0;i<=tots;++i) {dp[i]=1;}  //wrong initializing    for(i=0;i<=tots;++i) {dp[i]=0;}    p=root;    for(i=0;i<l;++i){        p=t[p].son[s[i]-'a'];        dp[p]++;    }    for(i=tots;i>=1;--i){        p=r[i];        if (t[p].fa!=-1)  dp[t[p].fa]+=dp[p];    }    for(i=1;i<=l;++i) f[i]=0;    for(i=1;i<=tots;++i)        if (dp[i]>f[t[i].len]) f[t[i].len]=dp[i];    for(i=l-1;i>=1;--i){        f[i]=f[i]<f[i+1]?f[i+1]:f[i];    }    for(i=1;i<=l;++i){        printf("%d\n",f[i]);    }    return 0;}


0 0
原创粉丝点击