ret, pos = false, -1 for i inrange(len(t) - len(s)): if s == t[i:i+len(s)]: ret, pos = true, i break return ret, pos
时间复杂度:每次子串对比消耗 ,一共对比 次,因此时间复杂度为:。
2.2 Karp-Rabin Algorithm
用对比 hash value 代替直接对比字符串:
1 2 3 4 5 6 7 8
ret, pos = false, -1 hs = hash(s) for i inrange(len(t)-len(s)): if hs == hash(t[i:i+len(s)]): if s == t[i:i+len(s)]: ret, pos = true, i break return ret, pos
时间复杂度:每次计算 hash value 消耗 ,如果 hash value 相等,需要再对比一次字符串,消耗 。总体时间复杂度与 simple algorithm 相同,在常数项上有所差异。在计算 hash(t[i:i+len(s)]) 时,我们已经见过了 t[i:i+len(s)] 中的所有字母,如果在计算 hash(t[i+1:i+1+len(s)]) 时,我们可以只考虑 t[i] 和 t[i+1+len(s)],那么时间复杂度降进一步降低。接下来看我们如何进一步将 次 hash value 计算复杂度从 降低到 。
2.2.1 Rolling Hash ADT
rolling hash ADT 维持一个字符串 的信息,支持 3 种方法:
r():获取当前字符串 的 hash value,即
r.append(c):往 的末尾增加一个字符
r.skip(c):从 的头部删除一个字符
利用 rolling hash ADT,上面的 string matching 算法可以改写成:
1 2 3 4 5 6 7 8 9 10 11 12
rs, rt = RollingHash(), RollingHash() for c in s: rs.append(c) for c in t[:len(s)]: rt.append(c) if rs() == rt(): #... for i inrange(len(s), len(t)): rt.skip(t[i-len(s)]) rt.append(t[i]) if rs() == rt(): #...