后缀自动机

后缀自动机

关键代码:


void ad(int x)
{
	int p=last,k=last=++tot;
	a[k].len=a[p].len+1;
	for(;p&&a[p].ch[x]==0;p=a[p].fa) a[p].ch[x]=k;
	if(!p) a[k].fa=1;
	else{
		int t1=a[p].ch[x];
		if(a[p].len+1==a[t1].len) a[k].fa=t1;
		else{
			int t2=++tot;
			a[t2]=a[t1];
			a[t2].len=a[p].len+1;
			a[t1].fa=a[k].fa=t2;
			for(;p&&a[p].ch[x]==t1;p=a[p].fa) a[p].ch[x]=t2;
		}
	}
}

水题清单

例题选记

P3804 【模板】后缀自动机 (SAM)

题意:

请你求出 S 的所有出现次数不为 1 的子串的出现次数乘上该子串长度的最大值。

做法:

把每次插入字符新建的节点时记为一,之后在parent树上将儿子的节点值累加到父亲上,该处的节点值即为此节点上所有字符串的出现次数,即可计算求出答案。

原理:

parent树上节点的祖先即为该节点后缀,但假如一个新字符时,即为该前缀的所有后缀的出现次数加 1,即让该节点上parent树上的所有祖先出现次数加 1.

P2408 不同子串个数

题意:

求本质不同子串个数。

做法:

枚举每个节点,该节点的 len 减去其父亲的 len 即为该节点代表的字符串的个数,累加求和即可。

题意:

求本质不同排名第k小的子串。

做法:

在建立出的后缀自动机的tire树上操作即可。

SP1811 LCS - Longest Common Substring

题意:

求2 个字符串的最长公共子串。

做法:

类似于KMP,在每次匹配失败时向上跳父亲,即为找其后缀,每一次跳完后比较答案。

P4248 [AHOI2013]差异

题意:

给定一个字符串,
求:
1⩽i<j⩽n∑​len(Ti​)+len(Tj​)−2×lcp(Ti​,Tj​)

lcp(a,b)表示字符串 a 和字符串 b 的最长公共前缀。

做法:

前两个一起算,lcp单独算。

将字符串翻转,即变为求每两个前缀的最长公共后缀。

任意两个前缀的最长后缀就在其 parent 树的 lca 上,因此在 parent 树上枚举此节点为多少节点的 lca 即可。记pt数组表示此节点是否表示前缀,siz为此子树上有多少个前缀。

[SDOI2016]生成魔咒

题意:

每插入一个字符时,当前本质不同的子串个数。

做法:

每插入一个字符,将其他节点向其连边时,将ans加上此节点代表子串数。

在字符类型过多时,在结构体里使用 map 存边。

posted @ 2021-07-31 21:00  ☄️ezuyz☄️  阅读(42)  评论(0)    收藏  举报