SPOJ8222 NSUBSTR - Substrings SAM
阿新 • • 發佈:2018-12-07
題意:
給你一個字串,求每個長度的字串的最多出現次數。串長<=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;
}