SP8222 NSUBSTR - Substrings(字尾自動機+dp)
阿新 • • 發佈:2018-12-11
解題思路
首先建出\(sam\),然後把\(siz\)集合通過拓撲排序算出來。對於每個點只更新它的\(maxlen\),然後再從大到小\(dp\)一次就行了。因為\(f[maxlen-1]>=f[maxlen]\)這個性質。
程式碼
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int MAXN = 250005; inline int max(int x,int y){ return x>y?x:y; } void out(int x){ if(!x) return;out(x/10);putchar('0'+x%10); } inline void OUT(int x){ if(!x) putchar('0'); else out(x); putchar('\n'); } char s[MAXN]; int n,lst,cnt,l[MAXN<<1],siz[MAXN<<1],fa[MAXN<<1],ch[MAXN<<1][27]; int a[MAXN<<1],c[MAXN<<1],f[MAXN]; inline void Insert(int c){ int p=lst,np=++cnt;lst=np;l[np]=l[p]+1; for(;p && !ch[p][c];p=fa[p]) ch[p][c]=np; if(!p) fa[np]=1; else { int q=ch[p][c]; if(l[q]==l[p]+1) fa[np]=q; else { int nq=++cnt;l[nq]=l[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[nq]=fa[q];fa[q]=fa[np]=nq; for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq; } } siz[np]=1; } int main(){ scanf("%s",s+1);lst=cnt=1; n=strlen(s+1); for(int i=1;i<=n;i++) Insert(s[i]-'a'+1); for(int i=1;i<=cnt;i++) c[l[i]]++; for(int i=1;i<=cnt;i++) c[i]+=c[i-1]; for(int i=1;i<=cnt;i++) a[c[l[i]]--]=i; for(int i=cnt;i;i--){ int p=a[i];siz[fa[p]]+=siz[p]; f[l[p]]=max(f[l[p]],siz[p]); } for(int i=n-1;i;i--) f[i]=max(f[i],f[i+1]); for(int i=1;i<=n;i++) OUT(f[i]); return 0; } /////