字符串合集I

1. 前缀函数与 KMP 算法

1.1 前缀函数

1.1.1 定义

给定一个长度为 n 的字符串 s,其 前缀函数 被定义为一个长度为 n 的数组 π

πi 是,子串 s[0i] 最长的相等的真前缀与真后缀的长度。

用式子表示如下:πi=maxk=0i{k:s[0k1]=s[i(k1)i]}

1.1.2 朴素求解

比较显然的 O(n3),很明显是可以优化的。

bool check(int l1, int r1, int l2, int r2) {
	for (int i = l1, j = l2; i <= r1, j <= r2; i++, j++)
		if (s[i] != s[j])
			return 0;
	return 1;
}
for (int i = 2; i <= n; i++)
	for (int j = i; j >= 1; j--)
		if (check(1, j, i - j + 1, i)) {
			pi[i] = j;
			break;
		}

1.1.3 优化一

我们通过观察 πi 的定义可以发现 πiπi1+1,比较显然,此时时间复杂度已降到 O(n2)

bool check(int l1, int r1, int l2, int r2) {
	for (int i = l1, j = l2; i <= r1, j <= r2; i++, j++)
		if (s[i] != s[j])
			return 0;
	return 1;
}
for (int i = 2; i <= n; i++)
	for (int j = pi[i - 1] + 1; j >= 1; j--)
		if (check(1, j, i - j + 1, i)) {
			pi[i] = j;
			break;
		}

1.1.4 优化二

假设我们已经求出了 π1 ~ πp,我们需要求 πp+1

  • 首先令 iπp,这表示 s[1 p + 1] 的最长相等前缀后缀一定由 s[1 p],的相等前缀后缀扩展而来,我们先尝试最长的那个相等前缀后缀。

  • 尝试匹配 si+1sp+1,若相等,则得到 πp+1=i+1

  • 若不满足条件,我们找次长的相等前缀后缀,也就是 πi,再回到上一步匹配后面一个字符。

此时我们的时间复杂度变为了 O(n)

for (int i = 2, p = 0; i <= n; i++) {
	while (p && s[p + 1] != s[i]) p = pi[p];
	pi[i] = p += s[p + 1] == s[i];
}

1.2 应用

1.2.1 Knuth-Morris-Pratt 算法

for (int i = 2, p = 0; i <= m; i++) {
	while (p && t[p + 1] != t[i]) p = nxt[p];
	nxt[i] = p += t[p + 1] == t[i];
}
for (int i = 1, p = 0; i <= n; i++) {
	while (p && t[p + 1] != s[i]) p = nxt[p];
	if (t[p + 1] == s[i]) p++;
	if (p == m) cout << i - m + 1 << '\n', p = nxt[p];
}

1.2.2 字符串的周期

对字符串 s 和 0 < p \le |s|,若 s[i] = s[i+p] 对所有 i \in [0, |s| - p - 1] 成立,则称 p 是 s 的周期。

对字符串 s 和 0 \le r < |s|,若 s 长度为 r 的前缀和长度为 r 的后缀相等,就称 s 长度为 r 的前缀是 s 的 border。

由 s 有长度为 r 的 border 可以推导出 |s|-r 是 s 的周期。

根据前缀函数的定义,可以得到 s 所有的 border 长度,即 \pi[n-1],\pi[\pi[n-1]-1], \ldots。2

所以根据前缀函数可以在 O(n) 的时间内计算出 s 所有的周期。其中,由于 \pi[n-1] 是 s 最长 border 的长度,所以 n - \pi[n-1] 是 s 的最小周期。

1.2.4 KMP 自动机

posted @   zhou_ziyi  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示