【字尾自動機】【SAM】學習相關以及模板
阿新 • • 發佈:2018-12-22
這是一篇偷懶的文章,主要是今天確實碰到了比較難理解的資料結構。
之前是以為所有的字尾自動機都可以用字尾陣列代替的,今天做到有一道題hdu6405,似乎並非如此了,主要這題是多串的,所以沒法子字尾陣列,只能字尾自動機(而且還是廣義字尾自動機,相關題解明天補上)
今天參考的博文
傳送門A
傳送門B
傳送門C
相關的理解和思考以及應用,以後會補上。
附上我的模板(其實是抄傳送門A的),當然這題可以用字尾陣列做。
//給定一個只包含小寫字母的字串SS,
//請你求出 SS 的所有出現次數不為 1 的子串的出現次數乘上該子串長度的最大值。
#include<cstdio>
#include <cstring>
#include<algorithm>
using namespace std;
using LL=long long;
const int N=1E6+5,M=2E6+10;
char s[N];
int cur,cnt,n,last,ch[M][26],lnk[M],dis[M],sz[M],c[N],sa[M];
LL ans;
void build_sam(int c, int id)
{
last=cur;
cur=++cnt;
int p=last;
dis[cur]=id;
for(;p&&!ch[p][c] ;p=lnk[p])
ch[p][c]=cur;
if(!p)
lnk[cur]=1;
else
{
int q=ch[p][c];
if(dis[q]==dis[p]+1)
lnk[cur]=q;
else
{
int nt=++cnt;
dis[nt]=dis[p]+1;
memcpy(ch[nt],ch[q],sizeof(ch[q]));
lnk[nt]=lnk[q];
lnk[q]=lnk[cur]=nt;
for(;ch[p][c]==q;p=lnk[p])
ch[p][c]=nt;
}
}
sz[ cur]=1;
}
void Flower()
{
for(int i=1;i<=cnt;i++)
c[dis[i]]++;
for(int i=1;i<=n;i++)
c[i]+=c[i-1];
for(int i=cnt;i;i--)
sa[c[dis[i]]--]=i; //技巧:相當於dis從大到小排序(這是dp順序),比nlogn快那麼點
for(int i=cnt;i;i--)
{
int p=sa[i];
if(sz[p]>1)
ans=max(ans,(LL)sz[p]*dis[p]); //dp
sz[lnk[p]]+=sz[p]; //dp
}
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
cur=cnt=1;
for(int i=1;i<=n;i++)
build_sam(s[i]-'a',i);
Flower();
printf("%lld",ans);
return 0;
}