[TOC]
kmp算法
kmp算法被用来做字符串的匹配。即 快速在 原字符串 中找到 匹配字符串
kmp算法的复杂度为O(m+n)
前缀: 包含首字母但不包含尾字母的所有子串。
后缀:包含尾字母但不包含首字母的所有子串。
匹配值为 前缀和后缀最大相同字串的长度
例:
A B C C A B
前缀 |
后缀 |
A |
B |
AB |
AB |
ABC |
CAB |
ABCC |
CCAB |
ABCCA |
BCCAB |
匹配值为 2 (AB)
所以逐步获取 该字符串的匹配表为:
A |
匹配值为 0 |
AB |
0 |
ABC |
0 |
ABCC |
0 |
ABCCA |
1 |
ABCCAB |
2 |
基本思路
当发现某一个字符不匹配时,利用之前遍历过的字符信息来避免暴力算法中的 “回退” 的步骤.
KMP 算法通过生成的next 数组,当匹配失败时 看最后匹配成功的字符所对应的next 数值p。
将字串patt的指针返回到 patt [p] 。(p代表字串中可以跳过的字串个数,也即对应字串中的匹配值)
例:
主串 A B A B A B C A A
子串 [A B A B C]
NEXT [0 0 1 2 0]
A |
B |
A |
B |
A |
B |
C |
A |
A |
A |
B |
A |
B |
C × |
|
|
|
|
0 |
0 |
1 |
2 |
0 |
|
|
|
|
当匹配到 子串 第一个数值时 发现不匹配。因此根据最后匹配成功的的字符 B 对应的next值 2 将指针指到 第2 个A 所在的位置继续匹配。(因为ABAB 匹配值为2 所以之前匹配成功的 AB 与 字串开头的两个 AB 完全一样,因此可以 跳过最长匹配字符 接着看之后数值是否匹配。)
A |
B |
A |
B |
A |
B |
C |
A |
A |
|
|
A |
B |
A |
B |
C |
|
|
|
|
0 |
0 |
1 |
2 |
0 |
|
|
不再需要回退主串中的指针,只需一次便利便可完成匹配 增加了效率
Next 数组 和 KMP算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| def build_next(patt): ''' 计算 Next数组 ''' next = [0] prefix_len = 0 i = 1 while i < len(patt): if patt[prefix_len] == patt[i]: prefix_len += 1 next.append(prefix_len) i += 1 else: if prefix_len == 0: next.append(0) i += 1 else: prefix_len = next[prefix_len - 1] return next
def kmp_search(string,patt): next = build_next(patt) i = 0 j = 0 while i < len(string): if string[i] == patt[j]: i += 1 j += 1 elif j > 0: j = next[j-1] else: i += 1 if j == len(patt): return i-j
|
例题
28. 找出字符串中第一个匹配项的下标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class Solution(object): def strStr(self, haystack, needle): """ :type haystack: str :type needle: str :rtype: int """ def build_next(patt): ''' 计算 Next数组 ''' next = [0] prefix_len = 0 i = 1 while i < len(patt): if patt[prefix_len] == patt[i]: prefix_len += 1 next.append(prefix_len) i += 1 else: if prefix_len == 0: next.append(0) i += 1 else: prefix_len = next[prefix_len - 1] return next next = build_next(needle) i = 0 j = 0 while i < len(haystack): if haystack[i] == needle[j]: i += 1 j += 1 elif j > 0: j = next[j-1] else: i += 1 if j == len(needle): return i-j if needle not in haystack: return -1
|