SAM 简记
SAM 是一张 DAG,满足对于
一个显然的想法是,把
如果定义
考虑如下过程:开始有一空串
显然,如果
parent tree 中的边,相当于从一个
常用的建图方法是每次往
先上代码:
int tot=1,lst=1;
void insert(int x){
int p=lst,np=lst=++tot;
tr[np].len=tr[p].len+1;
// Step1
for(;p&&!tr[p].ch[x];p=tr[p].fa) tr[p].ch[x]=np;
// Step2
if(!p) tr[np].fa=1; //Step 3
else{
int q=tr[p].ch[x];
if(tr[q].len==tr[p].len+1) tr[np].fa=q; //Step 4
else{
int nq=++tot;tr[nq]=tr[q];
tr[nq].len=tr[p].len+1;
tr[np].fa=tr[q].fa=nq;
for(;p&&tr[p].ch[x]==q;p=tr[p].fa) tr[p].ch[x]=nq; // Step5
}
}
}
该流程可分为五步。
方便起见,设加入了字符
一、新建一个节点
二、从长到短遍历旧串的每个后缀,如果加入
三、如果
四、如果
五、如果
模板题的完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
typedef long long ll;
int n;
char S[N];
struct Node{
int ch[26];
int len,fa,sum;
} tr[N<<1];
int tot=1,lst=1;
void insert(int x){
int p=lst,np=lst=++tot;
tr[np].sum++,tr[np].len=tr[p].len+1;
for(;p&&!tr[p].ch[x];p=tr[p].fa) tr[p].ch[x]=np;
if(!p) tr[np].fa=1;
else{
int q=tr[p].ch[x];
if(tr[q].len==tr[p].len+1) tr[np].fa=q;
else{
int nq=++tot;tr[nq]=tr[q],tr[nq].sum=0;
tr[nq].len=tr[p].len+1;
tr[np].fa=tr[q].fa=nq;
for(;p&&tr[p].ch[x]==q;p=tr[p].fa) tr[p].ch[x]=nq;
}
}
}
vector<int> son[N<<1];
void dfs(int u){
for(int v:son[u]) dfs(v),tr[u].sum+=tr[v].sum;
}
int main(){
scanf("%s",S+1),n=strlen(S+1);
for(int i=1;i<=n;i++) insert(S[i]-'a');
for(int i=2;i<=tot;i++) son[tr[i].fa].push_back(i);
dfs(1);
ll ans=0;
for(int i=1;i<=tot;i++) if(tr[i].sum!=1) ans=max(ans,(ll)tr[i].len*tr[i].sum);
printf("%lld",ans);
return 0;
}
SAM 极具巧思,值得仔细玩味。