KMP 模式匹配
时间复杂度:O(N + M)
求A是否为B的子串,并求出每次出现的匹配位置
1.求Next数组
对字符串A求自身匹配,求出Next数组,其中Next[i]表示A中以i为结尾(以i为后缀)的非前缀子串
与A的前缀能匹配的最大长度即:
Next[i] = max{j}, 其中i < j 且 a[i - j + 1 ~ i] = a[1 ~ j]
hint: 当不存在这样的j时 令Next[i] = 0;
步骤
- 初始化Next[1] = j = 0, 假设Next[1 ~ i - 1]已求出,下面求解Next[i]
- 利用前面的Next[1 ~ i - 1]进行状态转移,递推求Next[i]),不断尝试扩展匹配长度,
如果扩展失败(下一个字符不相等),令j = Nextj,
直至j为0(应该从头开始重新匹配) - 如果能扩展成功,匹配长度j ++。 Next[i]的值就是j
1 | char a[N], char b[M]; |
2.求f[]匹配数组
类似于Next[]数组的求法, 当j == n时需要 j = Next[j], 此时要缩短匹配的长度,
因为a[j]已经匹配越界
1 | void get_f() |
最小表示法
B[i]表示从i开始的循环同构字符串,即 s[i ~ n] + s[1 ~ i - 1]
算法概述:
求出一个字符串的旋转表示里的最小表示(将前缀接到后缀或后缀接到前缀)
思路详解
<
- 将字符串进行二倍扩展, 初始化i = 1, j = 2
- 通过直接向后扫描, 比较B[i] 与 b[j]两个循环同构串初始k - 0, k ++ 代表向后匹配
- k == n 则S只有一种字符构成, 任意B[i]都是最小表示
- 在 i + k处匹配失败
若 s[i + k] > s[j + k] i += k + 1
若 s[i + k] < s[j + k] j += k + 1
若 i == j 则 i ++ 或 j ++ 即可
3.i > n || j > n B[min(i, j)]即为最小表示, 否则重复第2步
<
1 | int a[2 * n]; //二倍 |