●SPOJ 8222 NSUBSTR - Substrings
題鏈:
http://www.spoj.com/problems/NSUBSTR/
題解:
後綴自動機的水好深啊!
懂不了相關證明,帶著結論把這個題做了。
看來這灘深水要以後再來了。
本題要用到一個叫 Right[P] 的數組,
表示 P對應的子串在原串中出現的所有位置的末尾位置下標的集合。
本題中,用這個數組存儲集合大小就好了,即 P對應的子串在原串中出現了Right[p]次。
而Right[P]的值,等於從改點出發到結束狀態的方案數。
但這個不好求,而是要用到另一個求法:用 Parent樹:
(暫時由結論可知)Right集合不相交,只存在並列和包含關系。
所以把原來的 pre[]的指向反向後,形成一棵樹,那麽父節點的Right值=sigma(子節點節點的Right)
又因為這個Parent樹無法遍歷,且註意到兒子節點的step一定大於父親節點的step,
所以對所有節點桶排序後,反向枚舉,去更新父親就好了。
計算出了Right[P]後,答案就可以統計了,詳見代碼。
代碼:
#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 500100
#define filein(x) freopen(#x".in","r",stdin);
#define fileout(x) freopen(#x".out","w",stdout);
using namespace std;
struct SAM{
int size,last,q,p,nq,np,len;
int pre[MAXN],step[MAXN],Right[MAXN],ch[MAXN][26];
int Newnode(int a,int b){
step[size]=a; memcpy(ch[size],ch[b],sizeof(ch[b]));
return size++;
}
void Extend(int x){
p=last; last=np=Newnode(step[p]+1,0);
while(p&&!ch[p][x]) ch[p][x]=np,p=pre[p];
if(!p) pre[np]=1;
else{
q=ch[p][x];
if(step[q]!=step[p]+1){
nq=Newnode(step[p]+1,q);
pre[nq]=pre[q]; pre[q]=pre[np]=nq;
while(p&&ch[p][x]==q) ch[p][x]=nq,p=pre[p];
}
else pre[np]=q;
}
}
void Make_Right(char *S){
static int c[MAXN],T[MAXN]; p=1;
for(int i=0;i<len;i++) p=ch[p][S[i]-‘a‘],Right[p]++;
memset(c,0,sizeof(c));
for(int i=1;i<size;i++) c[step[i]]++;
for(int i=1;i<=len;i++) c[i]+=c[i-1];
for(int i=1;i<size;i++) T[c[step[i]]--]=i;
for(int i=size-1;i;i--) Right[pre[T[i]]]+=Right[T[i]];
}
void Build(char *S){
len=strlen(S); memset(ch[0],0,sizeof(ch[0]));
size=1; last=Newnode(0,0); pre[last]=0;
for(int i=0;i<len;i++) Extend(S[i]-‘a‘);
Make_Right(S);
}
}suf;
int ANS[MAXN];
char S[MAXN];
int main()
{
scanf("%s",S);
int len=strlen(S);
suf.Build(S);
for(int i=1;i<suf.size;i++) ANS[suf.step[i]]=max(ANS[suf.step[i]],suf.Right[i]);
for(int i=len-1;i;i--) ANS[i]=max(ANS[i],ANS[i+1]);
for(int i=1;i<=len;i++) printf("%d\n",ANS[i]);
return 0;
}
●SPOJ 8222 NSUBSTR - Substrings