Luogu6139 【模板】广义后缀自动机(广义 SAM)
https://www.luogu.com.cn/problem/P6139
广义后缀自动机(广义 SAM)
后缀自动机可以处理单串的问题,而多串问题则需要广义后缀自动机。
广义后缀自动机可以用离线处理,也可以在线处理
离线:
将所有字符串建成一颗\(trie\)树,进行\(bfs\),依次将各节点丢进\(SAM\)中,此时的\(last\)为\(trie\)树中其父节点在\(SAM\)中的位置
C++ Code:
#include<bits/stdc++.h>
#define N 1000005
#define TN 2000005
#define son s[i]-'a'
using namespace std;
int n,tot=1,trie[N][26],t[TN][26],f[N],pre[TN],cnt=1,wz[TN],len[TN];
queue<int>q;
char s[N];
namespace Trie
{
void ins(char *s)
{
int n=strlen(s);
int u=1;
for (int i=0;i<n;i++)
{
if (!trie[u][son])
trie[u][son]=++tot,f[trie[u][son]]=u;
u=trie[u][son];
}
}
}
namespace GY_SAM
{
int np,p,q,g;
void Ins(int last,int u,int c)
{
np=++cnt;
len[np]=len[last]+1;
for (p=last;p&&!t[p][c];p=pre[p])
t[p][c]=np;
if (!p)
pre[np]=1; else
{
q=t[p][c];
if (len[p]+1==len[q])
pre[np]=q; else
{
g=++cnt;
len[g]=len[p]+1;
for (int i=0;i<26;i++)
t[g][i]=t[q][i];
pre[g]=pre[q];
for (;p&&t[p][c]==q;p=pre[p])
t[p][c]=g;
pre[np]=pre[q]=g;
}
}
wz[u]=np;
}
void Build()
{
::q.push(1);
wz[1]=1;
while (!::q.empty())
{
int u=::q.front();
::q.pop();
for (int i=0;i<26;i++)
if (trie[u][i])
{
::q.push(trie[u][i]);
Ins(wz[u],trie[u][i],i);
}
}
}
long long Query()
{
long long ans=0;
for (int i=2;i<=cnt;i++)
ans+=len[i]-len[pre[i]];
return ans;
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",s);
Trie::ins(s);
}
GY_SAM::Build();
cout << GY_SAM::Query() << endl;
return 0;
}
在线:
将每个字符串的初始\(last\)设为\(1\),进行操作
加特判(具体见此处)
C++ Code:
#include<bits/stdc++.h>
#define N 2000005
using namespace std;
int n,t[N][26],cnt=1,len[N],last=1,pre[N];
char s[N];
namespace GY_SAM
{
int np,p,q,g;
void ins(int c)
{
if (t[last][c])
{
p=last;
q=t[last][c];
if (len[p]+1==len[q])
{
last=q;
return;
} else
{
g=++cnt;
len[g]=len[p]+1;
for (int i=0;i<26;i++)
t[g][i]=t[q][i];
pre[g]=pre[q];
for (;p&&t[p][c]==q;p=pre[p])
t[p][c]=g;
pre[q]=g;
last=g;
return;
}
}
np=++cnt;
len[np]=len[last]+1;
for (p=last;p&&!t[p][c];p=pre[p])
t[p][c]=np;
if (!p)
pre[np]=1; else
{
q=t[p][c];
if (len[p]+1==len[q])
pre[np]=q; else
{
g=++cnt;
len[g]=len[p]+1;
for (int i=0;i<26;i++)
t[g][i]=t[q][i];
pre[g]=pre[q];
for (;p&&t[p][c]==q;p=pre[p])
t[p][c]=g;
pre[q]=pre[np]=g;
}
}
last=np;
}
void ins_string(char *s)
{
int n=strlen(s);
for (int i=0;i<n;i++)
ins(s[i]-'a');
}
long long query()
{
long long ans=0;
for (int i=2;i<=cnt;i++)
ans+=len[i]-len[pre[i]];
return ans;
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",s);
last=1;
GY_SAM::ins_string(s);
}
cout << GY_SAM::query() << endl;
return 0;
}