LC 3031. 将单词恢复初始状态所需的最短时间 II
https://leetcode.cn/problems/minimum-time-to-revert-word-to-initial-state-ii/description/
这题其实可以先从简单的版本开始做 https://leetcode.cn/problems/minimum-time-to-revert-word-to-initial-state-i/description/ 其实这题就是在找后缀的最长匹配,但是后缀的边界是在k个字符上。后面补上的字符其实完全可以根据之前匹配的情况进行补全。
class Solution:
def minimumTimeToInitialState(self, word: str, k: int) -> int:
i, ans = 0, 0
n = len(word)
while i < n:
i += k
ans += 1
if i < n and word[:n - i] == word[i:]:
break
return ans
对于小数据量版本,完全可以直接使用字符串检查的方式。但是如果字符串很长的话,那么每次检查字符串相等就比较耗时。一个办法是使用rolling hash function来做,另外一个则是使用类似kmp的算法。
这里如果使用kmp算法则需要稍微做一些改进,因为我们每次选择的后缀边界是在k个字符上。一个简单做法就是,我们可以先对k个字符进行group(最后一个group可能不是k个字符),然后使用kmp来比较每个group. 假设我们kmp算法得到的是 `max_match[i]` 表示i group的匹配前缀的最长长度是多少,那么答案就是 `len(max_match) - max_match[-1]`.
比如假设 max_match 的值是 [0,1,2,1,2,3] 的话,最后一个max_match[-1]是3,表示和前缀可以匹配3个groups, 那么说明向前数3个group可以完全可以前缀进行匹配,答案就是3.
class KMP:
@staticmethod
def build_max_match(t):
n = len(t)
match = [0] * n
c = 0
def eq(a, b):
sz = min(len(a), len(b))
return a[:sz] == b[:sz]
for i in range(1, n):
v = t[i]
while c and not eq(t[c], v):
c = match[c - 1]
if eq(t[c], v):
c += 1
match[i] = c
return match
class Solution:
def minimumTimeToInitialState(self, word: str, k: int) -> int:
w = []
i = 0
while i < len(word):
w.append(word[i:i + k])
i += k
match = KMP.build_max_match(w)
ans = len(match) - match[-1]
return ans