后缀自动机题目类型总结
Posted 。✧* ꧁王者꧂✧*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀自动机题目类型总结相关的知识,希望对你有一定的参考价值。
温馨提示:我很菜 , 写过的题不多 , 这里只是介绍一下后缀自动机常用的一些套路。
(以下均假设您已经会
S
A
M
SAM
SAM了)
1.
1.
1.找不同子串的个数:
这是
S
A
M
SAM
SAM比较常用的技巧 ,
S
A
M
SAM
SAM可以用来求这个的原因就是因为
S
A
M
SAM
SAM树上不表示相同的串 , 具体来说有两种方法:
F
i
r
s
t
:
First:
First:
p
a
r
e
n
t
parent
parent树上
D
P
DP
DP求解 , 求出
s
i
z
[
i
]
siz[i]
siz[i] , 即以
i
i
i为根的子树内有多少点。
S
e
c
o
n
d
:
Second:
Second:
∑
i
(
l
e
n
[
i
]
−
l
e
n
[
f
a
[
i
]
]
)
\\sum_i(len[i] - len[fa[i]])
∑i(len[i]−len[fa[i]]) , 因为每个节点表示的子串都不同 , 且每个点所表示的子串长度连续 , 所以
l
e
n
[
i
]
−
l
e
n
[
f
a
[
i
]
]
len[i] - len[fa[i]]
len[i]−len[fa[i]]即为点
i
i
i表示的子串个数。
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int last=1,tot=1;
long long f[N],ans;
struct NODE
int fa,len;
int ch[26];
node[N];
char s[N];
void insert(int x)
int p=last,np=last=++tot;
node[np].len=node[p].len+1;
for(;p&&!node[p].ch[x];p=node[p].fa) node[p].ch[x]=np;
if(!p) node[np].fa=1;
else
int q=node[p].ch[x];
if(node[q].len==node[p].len+1) node[np].fa=q;
else
int nq=++tot;
node[nq]=node[q];
node[nq].len=node[p].len+1;
node[q].fa=node[np].fa=nq;
for(;p&&node[p].ch[x]==q;p=node[p].fa) node[p].ch[x]=nq;
void dfs(int x)
f[x]=1;
for(int i=0;i<26;i++)
if(node[x].ch[i])
if(!f[node[x].ch[i]])
dfs(node[x].ch[i]);
f[x]+=f[node[x].ch[i]];
ans+=f[x];
int main()
freopen("sam2.in","r",stdin);
freopen("sam2.out","w",stdout);
scanf("%s",s+1);
for(int i=1;s[i];i++)
insert(s[i]-'a');
for(int i=2;i<=tot;i++)
ans+=node[i].len-node[node[i].fa].len;
cout<<ans;
return 0;
2.
2.
2.判断子串:
直接在
S
A
M
SAM
SAM树上跑即可。
3.
3.
3.原串所有子串中字典序第
k
k
k大(或小)的:
分为两种情况 , 重复字串算多次还是一次 。
如果只算一次的话 , 那么 , 先预处理出来
s
i
z
[
i
]
siz[i]
siz[i] , 然后
d
f
s
dfs
dfs直接找 。
如果可以算多次的话 , 就得预处理出每个节点的
e
n
d
p
o
s
endpos
endpos集合的大小 ,
e
n
d
p
o
s
endpos
endpos集合的大小即为此点表示的所有字符串出现的次数 。 只要把
s
i
z
[
i
]
siz[i]
siz[i]的含义改变一下即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
char s[N];
bool vis[N];
struct DODE
int len,fa;
int ch[26];
node[N];
struct E
int y,nex;
e[N];
int last=1,tot=1,z,k,head[N],num,f[N],sum[N];
void insert(int x)
int p=last,np=last=++tot;
f[tot]=1;
node[np].len=node[p].len+1;
for(;p&&!node[p].ch[x];p=node[p].fa) node[p].ch[x]=np;
if(!p) node[np].fa=1;
else
int q=node[p].ch[x];
if(node[q].len==node[p].len+1)
node[np].fa=q;
else
int nq=++tot;
node[nq]=node[q];
node[nq].len=node[p].len+1;
node[np].fa=node[q].fa=nq;
for(;p&&node[p].ch[x]==q;p=node[p].fa)
node[p].ch[x]=nq;
void dfs(int x,int K)
if(K<=f[x]) return ;
K-=f[x];
for(int i=0;i<26;i++)
if(node[x].ch[i])
if(K>sum[node[x].ch[i]])
K-=sum[node[x].ch[i]];
continue;
putchar(i+'a');
dfs(node[x].ch[i],K);
return;
void linjie(int x,int y)
e[++num]=(E)y,head[x];head[x]=num;
void dfs2(int x)
for(int i=head[x];i;i=e[i].nex)
int y=e[i].y;
dfs2(y);
f[x]+=f[y];
void dfs3(int x)
if(vis[x]) return;
vis[x]=1;
for(int i=0;i<26;i++)
int y=node[x].ch[i];
if(!y) continue;
dfs3(y);
sum[x]+=sum[y];
int main()
freopen("string.in","r",stdin);
freopen("string.out","w",stdout);
scanf("%s",s+1);
cin>>z>>k;
int n=strlen(s+1);
for(int i=1;i<=n;i++)
insert(s[i]-'a');
for(int i=2;i<=tot;i++)
linjie(node[i].fa,i);
dfs2(1);
for(int i=1;i<=tot;i++)
sum[i]= z==0?(f[i]=1):f[i];
f[1]=sum[1]=0;
dfs3(1);
if(sum[1]<k)
cout<<-1;
return 0;
dfs(1,k);
return 0;
4.
4.
4.判断两个串
S
S
S和
P
P
P的最长公共子串:
先构建出
S
S
S的后缀树 , 然后用类似
k
m
p
kmp
kmp匹配的方式 , 用
P
P
P去匹配 。 (为什么这里可以用类似
k
m
p
kmp
kmp的方法呢 ? 因为后缀树的每一个节点的
f
a
fa
fa都可以表示当前串的第一个
e
n
d
p
o
s
endpos
endpos不同的后缀 。)
5.
5.
5.判断多个串的最长公共子串长度:
在
4
4
4的基础上 , 以一个串为模式串建后缀树 , 其它串匹配 , 每一次都更新数组 , 并记录全局最大值 , 最后输出即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e4+10;
char s[N];
int last=1,tot=1,now[N],ans[N],sum,n,num,head[N];
struct NODE
int fa,len;
int ch[26];
node[N];
struct E
int y,nex;
e[N];
void insert(int x)
int p=last,np=last=++tot;
node[np].len=node[p].len+1;
for(;p&&!node[p].ch[x];p=node[p].fa) node[p].ch[x]=np;
if(!p) node[np].fa=1;
else
int q=node以上是关于后缀自动机题目类型总结的主要内容,如果未能解决你的问题,请参考以下文章