后缀自动机题目类型总结

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]]) ilen[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以上是关于后缀自动机题目类型总结的主要内容,如果未能解决你的问题,请参考以下文章

个人项目:自动生成四则运算题目总结

后缀自动机总结

后缀自动机总结

后缀自动机总结

后缀自动机 && 题目

后缀自动机总结