前置芝士: KMP, manachar
告示:本文字符串下标均从 $1$ 开始。
扩展KMP算法提供了一个计算 $Z$ 函数的方法。
求解 $Z$ 函数
定义 $Z$ 函数 $z_i$ 表示字符串 $s$ 以下标 $i$ 为开头的后缀与 $s$ 的最长公共前缀。
根据定义, $z_1 = n$, $n$ 为 $s$ 的长度。
如果直接暴力求解,显然每计算一次 $z_i$ 都要花费 $n$ 的时间,总时间是 $O(n^2)$。
不过可以通过扩展KMP算法,将此优化到 $O(n)$。
假设我们计算 $z_i$ 时, $1 \leq j < i$ 的 $z_j$ 已经全部算出。
我们可以维护一个 $r$ 最大的能与 $s$ 前缀匹配的$s[l:r]$。(这句话好好理解)
对于 $i \leq r$ 直接将 $z_i$ 初始成 $min(z_{i-l+1}, r-i+1)$, 否则为 $0$。
然后暴力往后匹配,求出 $z_i$, 更新之前的 $l,r$。
因为匹配次数跟 $r$ 相同, 所以是 $O(n)$ 的。
CODE
inline void getZ(char *s, int n) { for (int i = 1; i <= n; ++ i) z[i] = 0; z[1] = n; for (int i = 2, l = 0, r = 0; i <= n; ++ i) { if (i <= r) z[i] = min(z[i-l+1], r-i+1); while (i + z[i] <= n && s[i+z[i]] == s[z[i]+1]) ++ z[i]; if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1; } }View Code
求解文本串的后缀与模式串的匹配
根据上文, 我们可以先求出模式串的 $z$ 函数。
接着类似于 KMP, 我们对文本串开始对模式串进行匹配,发现计算方式与 $z$ 函数类似,于是只要改一下就行了。
CODE
inline void exkmp(char *s, int n, char *t, int m) { getZ(t, m); for (int i = 1; i <= n; ++ i) p[i] = 0; for (int i = 1, l = 0, r = 0; i <= n; ++ i) { if (i <= r) p[i] = min(z[i-l+1], r-i+1); while (i + p[i] <= n && s[i+p[i]] == t[p[i]+1]) ++ p[i]; if (i + p[i] - 1 > r) l = i, r = i + p[i] - 1; } }View Code
标签:匹配,函数,int,扩展,笔记,char,KMP From: https://www.cnblogs.com/CikL1099/p/17036890.html