Loading

【学习笔记】KMP 相关算法

Page Views Count

KMP

单模式串匹配,比较平凡所以不说了,比较有借鉴意义的每次拓展一位和 \(nxt\) 数组能极大减少不合法的匹配,时间复杂度 \(O(|s|+|t|)\)

引出一个定义,记满足 \(s[1,i]=s[|s|-i+1,|s|]\) 的前缀为字符串 \(s\)\(\mathrm{border}\),所有的 \(\mathrm{border}\) 构成 \(\mathrm{Border}(s)\) 集合。

失配树

KMP 处理 \(nxt\) 数组时是一个类似 \(nxt_i\leftarrow j\) 的操作,可以看作连边,根节点是 \(0\),一个前缀对应节点的全部祖先恰好是这个前缀的 \(\mathrm{Border}\) 集合。那么求两个前缀的最长公共 \(\mathrm{border}\) 就是树上 \(\mathrm{LCA}\),可能需要分是否是祖先关系讨论一下。

KMP 自动机

就是只有一个模式串版本的 AC 自动机,只要从 \(nxt_i\) 构建转移指针就行,目测用处不大。

Z 函数

又叫扩展 KMP。

算法思想

定义 Z 函数 \(z_i=\mathrm{lcp}(s,s[i,|s|])\),即后缀和原串的 \(\mathrm{lcp}\)

考虑维护当前最右端的区间 \([l,r]\),扩展的方法类似 Manacher:

  • \(i>r\),直接暴力拓展;

  • \(i\le r\),那么 \(s[1,r-l+1]=s[l,r]\),于是 \(s[i-l+1,r-l+1]=s[i,r]\),那么 \(z_i\ge \min(z_{i-l+1},r-i+1)\),设为初始值再暴力拓展即可。

复杂度是线性的,证明考虑暴力拓展非 \(O(1)\) 次时 \(r\) 必然增大。

例题

CodeForces-432D Prefixes and suffixes *2000

\(z_i=|s|-i+1\) 的就是 \(\mathrm{border}\),查询出现次数就是 \(z_i\ge l\) 的位置个数。

Periodicity Lemma

定义 \(p\)\(s\) 的一个周期当且仅当对于 \(1\le i\le |s|-p\),有 \(s_i=s_{i+p}\)。感性理解就是周期就是一个字符串划分成若干相等的段,最后一段可以是这些段的一个前缀。

Weak Periodicity Lemma:若 \(p,q\) 都是字符串 \(s\) 的周期且 \(p+q\le |s|\),则 \(\gcd(p,q)\) 也是 \(s\) 的周期。

直观来看 \(p,q\) 导出 \(\gcd(p,q)\) 是类似辗转相除的跳来跳去,存在一个问题:如果将字符串按照两个周期分别补充成无限长,得到的结果可能不相等,例如 \(\texttt{abaababa}\) 如果按 \(\texttt{abaab}\)\(\texttt{abaabab}\) 补充肯定是不相等的,那么跳来跳去就可能出错,所以有一个长度的限制。

弱周期引理的证明比较简单,考虑归纳,规定 \(p\le q\),假设已经证明对于任意 \(x<p,y<q\) 都是成立的,而 \(p=1\) 时显然也成立。由于 \(p+q\le |s|\)\(p\le q\),可以将 \(1\le i\le |s|\) 划分成 \(i\le |s|-q\)\(i>|s|-q\) 两部分。对于前者,\(i+q\)\(i+q-p\) 都在 \([1,|s|]\) 范围内,于是 \(s_i=s_{i+q-p}\);对于后者,实际考虑 \(p\le |s|-q<i\le |s|-(q-p)\)\(i-p\)\(i-p+q\) 都在范围内,于是 \(s_i=s_{i-p+q}\)\(q-p\) 就是 \(s\) 的一个周期,而根据归纳 \(\gcd(q-p,p)=\gcd(p,q)\) 也是 \(s\) 的一个周期。


Periodicity Lemma:若 \(p,q\) 都是字符串 \(s\) 的周期且 \(p+q-\gcd(p,q)\le |s|\),则 \(\gcd(p,q)\) 也是 \(s\) 的周期。

有一个生成函数证明,感觉很有趣。记 \(P(x),Q(x)\) 分别为长度为 \(p,q\) 的周期,\(S_p(x),S_q(x)\) 分别为按照周期补充的无限长串,那么满足:

\[S_p(x)=\dfrac{P(x)}{1-x^p}\equiv S(x)\pmod {x^{|s|}} \]

\[S_q(x)=\dfrac{Q(x)}{1-x^q}\equiv S(x)\pmod {x^{|s|}} \]

于是可以得到:

\[{S_p(x)-S_q(x)=\dfrac{P(x)}{1-x^p}-\dfrac{Q(x)}{1-x^q}=\dfrac{1-x^{\gcd(p,q)}}{(1-x^p)(1-x^q)}\left(\dfrac{P(x)(1-x^q)}{1-x^{\gcd(p,q)}}-\dfrac{Q(x)(1-x^p)}{1-x^{\gcd(p,q)}}\right)} \]

把后面的东西记作 \(F(x)\),观察可知 \(\deg F(x)<p+q-\gcd(p,q)\le |s|\),而 \(S_p(x)-S_q(x)\equiv 0\pmod {x^{|s|}}\),所以 \(F(x)\equiv 0 \pmod{x^{|s|}}\),根据其度数知 \(F(x)=0\),所以 \(S_p(x)=S_q(x)\),不需要截取,所以后面就能按照辗转相除证明了。

Border Series

内容

主要是证明一个性质:一个字符串 \(s\)\(\mathrm{Border}\) 可以划分为 \(O(\log n)\) 个等差数列。

首先考虑任意一个 \(\mathrm{border}\) \(s[1,i]\),发现 \(s\) 有长为 \(|s|-i\) 的周期,这个画一画图就容易得到。

那么取出所有长度 \(\ge \left\lceil\frac{|s|}{2}\right\rceil\)\(\mathrm{border}\),其中最长的一个就对应了 \(s\) 的最小周期 \(p\),发现取任意 \(p\) 的倍数也能得到一个周期,现在需要证明这些周期中不含不是 \(p\) 的倍数且夹在两个倍数之间的。考虑另一个周期 \(q\),若 \(p+q\le |s|\),即满足 Weak Periodicity Lemma,那么一定有 \(p\mid q\),否则 \(\gcd(p,q)<p\),那么 \(p\) 就不是最小周期了。这里取出的长度为 \(\ge \left\lceil\frac{|s|}{2}\right\rceil\)\(\mathrm{border}\) 对应的周期一定 \(\le \left\lfloor\frac{|s|}{2}\right\rfloor\),因此二者之和一定满足 Weak Periodicity Lemma 的限制。

于是证明了所有长度 \(\ge \left\lceil\frac{|s|}{2}\right\rceil\)\(\mathrm{border}\) 构成了一个公差为最小周期的等差数列,考虑找到剩下的 \(\mathrm{border}\) 中最长的一个,将其作为整个串,继续这样处理,只需要 \(O(\log n)\) 次就结束了。

之后不妨再来看看放在失配树是什么样子:就是把根到 \(n\) 节点链划分成 \(O(\log n)\) 条等差数列,同时如果失配树上有分叉,那么一定是某个等差数列的首项。

例题

Luogu-P5829 失配树

考虑不用失配树做。

考虑当前节点 \(x\),记 \(d=x-nxt_x\),若 \(2d\ge x\),则再往下减少 \(\mathrm{border}\) 的长度就不符合长度大于二倍的要求了,所以 \(x\) 就是链顶;否则取 \(x\bmod d+d\) 就是链顶,每次选链顶编号大的跳上去。

Luogu-P4156 WC 2016 论战捆竹竿

相当于每次加上除去 \(\mathrm{border}\) 的部分,是同余最短路问题。

降低复杂度需要从 Border Series 入手,得到 \(O(\log n)\) 个三元组 \((a,d,l)\),表示可以增加长度为 \(a+id\) 的字符串,其中 \(i\in [0,l]\)

对每种等差数列依次考虑肯定是正确的,对三元组 \((a,d,l)\) 求模 \(a\) 的同余最短路,转移会形成 \(\gcd(a,d)\) 个环,可以对这些环分别考虑。其中转移前的最小值一定不会被转移,也不会有其他转移跨过这个最小值,可以从这个位置断开,之后转移只剩 \(l\) 的限制,可以单调队列优化。

之后考虑如何把模 \(a\) 的同余最短路转成模 \(a'\) 的同余最短路,不妨看做多源,初始值 \(f_i\rightarrow g_{f_i\bmod a'}\),同样也形成了 \(\gcd(a,a')\) 个环,选最小值断开,此时转移没有任何限制,直接前缀 \(\min\) 优化就行了。

参考资料

Z 函数

周期引理

Border Series

posted @ 2024-01-10 09:58  SoyTony  阅读(107)  评论(3编辑  收藏  举报