spoj 8222 Substrings(NSUBSTR),后缀自动机
2014-08-17 18:25
344 查看
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问题),就用父边转移(后缀树性质)。
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 250009 int root,last;//sam int 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*2 void 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; }
相关文章推荐
- 【SPOJ】1812. Longest Common Substring II(后缀自动机)
- SPOJ - LCS Longest Common Substring 后缀自动机
- spoj 1811 LCS - Longest Common Substring (后缀自动机)
- spoj 1811 LCS (后缀自动机 SAM)
- 后缀自动机(SAM) :SPOJ LCS - Longest Common Substring
- SPOJ NSUBSTR(后缀自动机)
- SPOJ 8222 Substrings 后缀自动机入门
- SPOJ LCS(Longest Common Substring-后缀自动机-结点的Parent包含关系)
- ●SPOJ 8222 NSUBSTR–Substrings(后缀自动机)
- spoj 8222 Substrings (后缀自动机)
- SPOJ 8222. Substrings(后缀自动机模板)
- SPOJ NSUBSTR Substrings 后缀自动机
- 【SPOJ】Distinct Substrings(后缀自动机)
- 后缀自动机1002 SPOJ NSUBSTR
- SPOJ 7258 Lexicographical Substring Search [后缀自动机 DP]
- SPOJ NSUBSTR Substrings ——后缀自动机
- SPOJ - LCS2 Longest Common Substring II 后缀自动机
- SPOJ1811 [后缀自动机]
- 后缀自动机小结 (spoj 8222)
- spoj 1812 LCS2 - Longest Common Substring II (后缀自动机)