字符串匹配 之 拓展 KMP算法(Z算法)
文章目录
- 习题
- 2223.构造字符串的总得分和
- 3031.将单词恢复初始状态所需的最短时间 II
灵神代码模版
-
区别与
KMP
算法KMP
算法可用于求解在线性时间复杂度0(n)
内求解模式串p在主串s中匹配的未知
- 当然,由于在
KMP
算法中,预处理求解出了next
数组,也就是可以求解出字符串p的真后缀与真前缀的最大公共前缀LCP
,next[i]
表示字符串p[0] 到 p[i] 的真前缀和真后缀的LCP
-
拓展KMP
算法(Z函数
):- 用于求解
主串s的后缀与模式串p的LCP
- 当然,预处理过程中的
z[i]
可用于求解字符串p[i:] 与字符串p的LCP
- 用于求解
下面给出求解
Z数组
的python 代码
# 计算并返回 z 数组,其中 z[i] = |LCP(s[i:], s)|
def calc_z(s: str) -> List[int]:n = len(s)z = [0] * n# box_l和box_r维护的是一个区间,# 也就是s[box_l:box_r+1]与s[box_r]匹配box_l = box_r = 0for i in range(1, n):# 当i在区间内,就可以利用先前的信息if i <= box_r:# 这个z[i-box_l] 十分巧妙z[i] = min(z[i - box_l], box_r - i + 1)while i + z[i] < n and s[z[i]] == s[i + z[i]]:box_l, box_r = i, i + z[i]z[i] += 1z[0] = nreturn z
- 重点分析
为什么落在区间的时候
,有z[i]=min(z[i - box_l],box_r - i + 1)
- 首先,对于
i-box_l
,是左边的情况,我们得时刻记得s[box_l:box_r+1]与s[box_r]匹配
,所以,我们更加关注从s[i]
开始的情况,也就是s[box_l]
开始的情况,所以可以直接借鉴 - 对于
box_r-i+1
,是对于区间提供信息的长度限制,不能超过box_r-i+1
,因为超过这个范围的信息没有记录
- 首先,对于
习题
2223.构造字符串的总得分和
2223.构造字符串的总得分和
- 思路分析:
拓展kmp算法模版题目
class Solution:def sumScores(self, s: str) -> int:# 拓展kmp算法的模版题目n = len(s)z = [0]*n box_l,box_r =0,0for i in range(1,n):if i <= box_r:z[i] = min(z[i-box_l],box_r-i+1)while i + z[i] < n and s[z[i]] == s[i + z[i]]:box_l,box_r = i,i+z[i]z[i] += 1z[0] = n return sum(z)
3031.将单词恢复初始状态所需的最短时间 II
3031.将单词恢复初始状态所需的最短时间 II
灵神题解
- 思路分析:我们需要考虑原始序列,与当前位置的后缀的最长公共前缀的长度关系,如果
z[i]>=n-i
,并且i%k==0
,就说明可以通过i//k
次就可以通过恢复原型,当然,如果遇到无法恢复的情况,我们至多 ⌈ n k ⌉ \left\lceil\frac{n}{k}\right\rceil ⌈kn⌉次即可恢复
class Solution:def minimumTimeToInitialState(self, word: str, k: int) -> int:# 拓展kmp算法问题# 反正就是查看z[k]是否等于k ,一直找n = len(word)z = [0]*n box_l,box_r = 0,0for i in range(1,n):if i <= box_r:z[i] = min(z[i-box_l],box_r-i+1)while i + z[i] < n and word[z[i]] == word[i+z[i]]:box_l,box_r = i,i+z[i]z[i] += 1if i % k == 0 and z[i] >= n - i:return i // k# 如果复原不了,那也只是 n // k 的向上取整return (n-1) // k + 1