再学串串(四):后缀是后缀的后缀是后缀的后缀
花了近一周憋了个大的,发现错误或者有建议私信评论反馈都可以,会尽快响应。
过去的章节
- 再学串串(一):我真的学会 KMP 了吗?评「clx201022 式的傲慢」
- 再学串串(二):AC 自动机不是自动 AC 机
- 再学串串(三):被构造自动机上计数 DP 题吓晕
目前已同步在博客园更新。
上文评论区中某人建议道:
该进军基本子串结构了
但我不会,所以接下来是 SAM(Suffix AutoMaton,后缀自动机)和 SA(Suffix Array,后缀数组)。
:::info[马拉车问了吗?]
马拉车速度没有万用自动机快,所以要再等一会。
:::
第四站:后缀自动机——不止后缀
一个后缀自动机(SAM)是接受特定字符串
我想说的是,自动机是一个很广泛的概念,你不会的东西是构造一个能精准刻画题目条件的、状态数尽量少的、(有限状态)自动机,而不是不会字符串算法,换成别的条件例如子序列个数也能用有限状态自动机刻画
这句话很好地体现了后缀自动机的核心:最小。
字典树 Trie 在图论意义下是一棵树——其每个状态
x 经过任意字符串转移到的所有状态都不可能在非x 子树的任何其他位置。这个性质使得字典树易于维护;但同时,这个性质也使得字典树在所有等价自动机^dengjia中往往不是最小的。
如果只需要能构造出即可,那么字典树就够用了。但由于是最小,才有了「后缀自动机」这个概念的出现。
后缀自动机虽然名字上带个「后缀」,但它首先是个「自动机」,这就注定了它是带有「前缀性」的。而前缀的后缀,就是任意子串了。所以,你也可以叫它「子串自动机」。
性质
只说「最小」有些笼统,也不利于系统地构造后缀自动机的概念,所以接下来尝试思考一个已经构造好的后缀自动机会有哪些性质。
如果没有具体的例子的话性质是较难找的,因此从别的地方借(偷)一个现成的后缀自动机进行研究。
对字符串
不同于字典树是树,KMP 自动机可能有环,后缀自动机是一个一般意义上的 DAG。
自动机上红框标注的为 接受状态。
起点开始的每条路径都与一个
:::info[证明]
设
要证明的命题成立当且仅当以下两个命题成立:
- 起点开始的每条路径必然有一个
T 的子串相对应。 - 每一个
T 的子串必然有一条起点开始的路径相对应。
假设存在一个从起点开始的每个路径没有对应的
假设存在一个
Q.E.D.
:::
发现无论是
答案很简单:不需要区分。
等价类
在样例
令
关于
- 如果
A 是B 的后缀,那么endpos_B \subseteq endpos_A 。 -
- 设
|A|\le|B| ,则endpos_A\cap endpos_B=\varnothing 当且仅当A 不是B 的后缀。 - 一个非空等价类
E 内,必然存在一个最长串B ,其余所有A\in E 都是B 的后缀。 - 若
B 的后缀B[l,|B|] 和B[r,|B|] 同属于一个等价类E ,则\forall l\le x\le r,B[x,|B|]\in E 。
(具体证明可见这篇文章)
由上述引理可以得出,一个等价类应该是形如
如果说 KMP 自动机中状态的含义是文本串
由此,后缀自动机状态转移的思路也就明确了:根据目前的
而对于一个状态
如仅由
其中「缩小范围」提醒我们:往后添加字符的过程,同时也是排除最终子串
由此可以容易推出一个跟前后缀拼接有关的结论:
- 若
A+B=C ,则有endpos_C \subseteq \{x+|B|\}(x\in endpos_A) 。
后缀链接
仿照 KMP 自动机的
任务就转化为了已对
因为每个点都对应一个等价类,所以点的变化即是等价类的变化,而等价类的变化即对应着
- 设
E_{endpos_X} 中存在最短串Z ,有|Z|=|Y|+1 。
即
:::info[引理
对
对
综上,
Q.E.D.
:::
在 引理
- 对于状态
p 和其通过c 转移到的状态q=\delta(p,c) ,有\forall P\in E_p[\exist Q\in E_q[Q=P+c]] ,也即E_q\supseteq \{P+c|P\in E_p\} ,取等当且仅当不存在另一个p'\ne p 也满足q=\delta(p',c) 。若存在p' ,则要么p' 在p 的后缀链接树到根的路径上,要么p 在p' 的后缀链接树到根的路径上;这等价于要么E_p 所有元素全是E_{p'} 的后缀,要么E_{p'} 所有元素全是E_{p} 的后缀。
证明可由读者根据等价类与状态转移之间的关系自行完成。
状态转移
类比于 KMP 的跳
根据 引理
思考
平凡地,每次必然新增一个等价类
如何处理
设
对于
而对于
显然
构造
依照前文,从
不断上溯并对未定义的
如果一直上溯到
重复直到存在
:::info[证明]
此时
Q.E.D.
:::
否则
剩下原来的状态内
然后所有
由于
对于每个状态维护一下对应等价类的最长长度、后缀链接和转移边然后照上文步骤实现即可。
以
感兴趣的读者可以试着自己推一推。
程序实现
:::success[P3804 【模板】后缀自动机(SAM)]
每个等价类内考虑最长串即可,注意考虑后缀链接代表的等价类间的后缀包含关系。(与 AC 自动机匹配要考虑
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6;
string S;
struct state
{
int len, link;
unordered_map<char, int> to;
state(int len = 0, int link = 0, unordered_map<char, int> to = unordered_map<char, int>()) : len(len), link(link), to(to){}
};
state st[maxn << 1 | 1];
int st_cnt, lst;
vector<int> e[maxn << 1 | 1];
int siz[maxn << 1 | 1];
void sam_extend(char c)
{
int cur = ++st_cnt;
siz[cur] = 1;
st[cur].len = st[lst].len + 1;
int p = lst;
while (p && !st[p].to.count(c))
{
st[p].to[c] = cur;
p = st[p].link;
}
if (!p)
{
st[cur].link = 1;
}
else
{
int q = st[p].to[c];
if (st[p].len + 1 == st[q].len)
{
st[cur].link = q;
}
else
{
int qci = ++st_cnt;
st[qci] = st[q];
swap(q, qci); // 因为 link。。。
st[q].len = st[p].len + 1;
// q 是原等价类里 |Q| <= |P| + 1 的(P 为 p 等价类内最长串)
// qci 就是 |Q| > |P| + 1 的
while (p && st[p].to[c] == qci)
{
st[p].to[c] = q;
p = st[p].link;
}
st[qci].link = st[cur].link = q;
}
}
lst = cur;
}
ll ans = 0;
void dfs(int u)
{
for (auto v : e[u])
{
dfs(v);
siz[u] += siz[v];
}
if (siz[u] != 1) ans = max(ans, 1ll * siz[u] * st[u].len); // 出现次数不为 1
}
void get_SAM()
{
st[lst = st_cnt = 1] = state();
for (int i = 0; i < S.size(); i++) sam_extend(S[i]);
for (int i = 2; i <= st_cnt; i++) e[st[i].link].emplace_back(i); /*
for (int i = 1; i <= st_cnt; i++)
{
cout << i << ' ' << st[i].link << " link" << endl;
for(char c = 'a'; c <= 'z'; c++)
{
if(st[i].to.count(c)) cout << i << ' ' << st[i].to[c] << string(" ") + c << endl;
}
}
*/
dfs(1);
}
int main()
{
cin >> S;
get_SAM();
cout << ans;
return 0;
}
:::
由于篇幅已经较长且信息量极大,所以关于 SA 的内容只能且听下回分解了。
友链
- 《后缀自动机(SAM)学习笔记》,@KobeBeanBryantCox
- 《后缀自动机(SAM)奶妈式教程》,ZTer
- OI Wiki
- Graph Editor
警示后人
你知道吗不要在晚上睡觉前听求&影并且开单曲循环不然每次碰到 Q.E.D. 的时候都会忍不住唱出来。
创作声明
本文遵循 CC BY-NC-SA 4.0 协议。
保证本文未使用任何 AI 工具辅助创作。
转载例如下:
[原文](https://www.luogu.com.cn/article/b3zc5pv4)作者为 [clx201022](https://www.luogu.com.cn/user/552688),转载人保证会遵循 [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh-hans) 协议:以适当的方式署名,不以盈利为目的进行转载,并以相同方式共享此文。
[^linktree]: 本文中提及的后缀链接树与这个 linktree 没有任何关系。