1. 程式人生 > >SPOJ8222 NSUBSTR - Substrings SAM

SPOJ8222 NSUBSTR - Substrings SAM

題目連結

題意:
給你一個字串,求每個長度的字串的最多出現次數。串長<=250000

題解:
還是一個SAM題。

應該做了之前那麼多題之後,這個也不是那麼難想了。對於每個串,長度應該是每個點的right集合大小。我們對原串上的位置是SAM上標記,然後在parent樹上dp,求出每個點的right集合的大小。然後對於長度相同的串,我們把他們的right集合大小取max就好了。

程式碼:

//帶註釋的兩個位置的寫法均可 
#include <bits/stdc++.h>
using namespace std;

int n,fa[1000010],ch[1000010][26],
cnt=1,rt=1,lst=1,len[1000010]; int sz[1000010],f[1000010],a[1000010],rk[1000010]; char s[1000010]; inline void insert(int x) { int cur=++cnt,pre=lst; lst=cur; len[cur]=len[pre]+1; sz[cur]=1;// for(;pre&&!ch[pre][x];pre=fa[pre]) ch[pre][x]=cur; if(!pre) fa[cur]=rt; else { int ji=ch[pre][x]; if(len[ji]==len[pre]
+1) fa[cur]=ji; else { int gg=++cnt; len[gg]=len[pre]+1; memcpy(ch[gg],ch[ji],sizeof(ch[ji])); fa[gg]=fa[ji]; fa[ji]=fa[cur]=gg; for(;pre&&ch[pre][x]==ji;pre=fa[pre]) ch[pre][x]=gg; } } } int main() { scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;++i) insert
(s[i]-'a'); for(int i=1;i<=cnt;++i) a[len[i]]++; for(int i=1;i<=n;++i) a[i]+=a[i-1]; for(int i=cnt;i>=1;--i) rk[a[len[i]]--]=i; /* int ji=rt; for(int i=1;i<=n;++i) { int x=s[i]-'a'; ji=ch[ji][x]; ++sz[ji]; }*/ for(int i=cnt;i>=1;--i) sz[fa[rk[i]]]+=sz[rk[i]]; for(int i=1;i<=cnt;++i) f[len[i]]=max(f[len[i]],sz[i]); for(int i=n;i>=1;--i) f[i]=max(f[i],f[i+1]); for(int i=1;i<=n;++i) printf("%d\n",f[i]); return 0; }