标签:strong last efi length ref 题目 lib cst 后缀自动机
http://www.spoj.com/problems/NSUBSTR/ (题目链接)
给出一个字符串S,令${F(x)}$表示S的所有长度为x的子串出现次数的最大值。求${F(1)......F(length(S))}$
后缀自动机例题,下面写几点自己认为理解后缀自动机的重点。
对于这道题,我们需要做的就是计算SAM中每个节点的Right集合的大小,即在串中的出现次数。因为parent树的某个节点Right集合是它父亲的真子集,所以我们考虑从parent树的底端向上不断更新祖先的Right集合。
代码模着hzwer写的,加了点注释。
// spoj8222 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #define LL long long #define inf 1<<30 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=500010; int n,last,Dargen,cnt; int ch[maxn][26],a[maxn],b[maxn],t[maxn],f[maxn],l[maxn],r[maxn],fa[maxn]; char s[maxn]; void add(int x) { int c=a[x]; int p=last,np=++cnt;last=np; //p是上次插入的节点,np为现在正在插入的这个节点,last变成了np l[np]=x; //Max(s),也就是到达这个节点的最长的子串(相当于x的前缀的长度→_→) for (;p && !ch[p][c];p=fa[p]) ch[p][c]=np; //p以及其在parent树上的祖先没有c边的全部连边(可以从right集合的角度去考虑) if (!p) fa[np]=Dargen; //如果都没有c边,则np的parent为dargen————情况1 else { int q=ch[p][c]; //找到了深度最深的有c边的祖先 if (l[q]==l[p]+1) fa[np]=q; //情况2 else { //情况3 int nq=++cnt;l[nq]=l[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[nq]=fa[q]; fa[np]=fa[q]=nq; for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } } int main() { scanf("%s",s+1); int len=strlen(s+1); for (int i=1;i<=len;i++) a[i]=s[i]-‘a‘; last=Dargen=++cnt; for (int i=1;i<=len;i++) add(i); for (int p=Dargen,i=1;i<=len;i++) p=ch[p][a[i]],r[p]++; //先将主链上的right全部加1 for (int i=1;i<=cnt;i++) b[l[i]]++; //按照l[x]从小到大基数排序 for (int i=1;i<=len;i++) b[i]+=b[i-1]; for (int i=1;i<=cnt;i++) t[b[l[i]]--]=i; for (int i=cnt;i>=1;i--) r[fa[t[i]]]+=r[t[i]]; //从后往前for,更新parent的right大小 for (int i=1;i<=cnt;i++) f[l[i]]=max(f[l[i]],r[i]); //更新答案 for (int i=len;i>=1;i--) f[i]=max(f[i],f[i+1]); for (int i=1;i<=len;i++) printf("%d\n",f[i]); return 0; }
标签:strong last efi length ref 题目 lib cst 后缀自动机
原文地址:http://www.cnblogs.com/MashiroSky/p/6287433.html