问题定义:给定字符串string: abcdeabcea 以及匹配字符串parttern: abce 问 parttern是否为string的子串,如果是请返回第一次出现索引的位置,否则返回-1
以下是暴力匹配的代码:
class Solution: def match(self, string,parttern): len1,len2 = len(string),len(parttern) i,j = 0,0 while i<len1 and j<len2: if string[i]==string[j]: i+=1 j+=1 else: i=i-j+1 j=0 if j==len2: return i-j else: return -1
KMP算法:
暴力匹配算法是当string和parttern不匹配时,返回到string的下一个字符依次重新开始匹配。
而KMP算法遇到string和parttern不匹配时,partttern向右移动。从而节约了时间。
以下是KMP的代码:
1 class Solution: 2 def KMPmatch(self, string,parttern): 3 len1,len2 = len(string),len(parttern) 4 i,j = 0,0 5 while i<len1 and j<len2: 6 if j==-1 or string[i]==parttern[j]: 7 i+=1 8 j+=1 9 else: 10 j = nextp[j] 11 if j==len2: 12 return i-j 13 else: 14 return -1
KMP算法的描述:
1: 当 j == -1 或者 匹配成功时 ,继续匹配
2: 当 j!= -1 且不匹配时 i 不变, j = nextp[j] (也就是 parttern向右移动了 j- next[p]个位置)
下面介绍 nextp
模式串parttern 为 abab
则
模式串 | a | b | a | b |
最大前缀后缀公共元素长度 | 0 | 0 | 1 | 2 |
ab的前缀为 a ,后缀为b 因此表格1,2为0
aba的前缀为 a,ab 后缀为 ba,a 公共元素为a 因此表格为1
abab的前缀为 a , ab , aba , 后缀为 bab, ab,b 公共元素ab 因此表格为2
nextp 即
模式串 |
a | b | a | b |
nexp | -1 | 0 | 0 | 1 |
nextp数组就是将表格1中的最大前缀后缀公共元素长度的初值设为-1, 然后整体右移.
nextp和最大长度表是等价的
我们计算parttern向右移动的长度时.
如果是用的最大长度表, 就是 j-当前不匹配元素的上一个元素在最大长度表中的值, 即 j-common[j-1]
如果用的是nextp表,就是 j - next[j]
构造数组nextp
nextp通过递归的方式求得. 假设我们已经知道 nextp[j]的值为k, 那么nextp[j+1]的值为:
如果 parttern[j] == parttern[k] 则 nextp[j+1] = next[j] + 1 = k+1
如果 parttern[j] != parttern[k] 则 迭代 k = next[k]判断 parttern[k] ?= parttern[j] 直到相等或者 next[k]=-1为止.
以下为整个代码部分.python
1 class Solution: 2 def getnext(self,parttern): 3 nextp = [0]*len(parttern) 4 nextp[0] = -1 5 k,j = -1,0 6 while j<len(parttern)-1: 7 if k==-1 or parttern[j]==parttern[k]: 8 j+=1 9 k+=1 10 nextp[j] = k 11 else: 12 k = nextp[k] 13 return nextp 14 15 def KMPmatch(self, string,parttern): 16 len1,len2 = len(string),len(parttern) 17 nextp = self.getnext(parttern) 18 i,j = 0,0 19 while i<len1 and j<len2: 20 if j==-1 or string[i]==parttern[j]: 21 i+=1 22 j+=1 23 else: 24 j = nextp[j] 25 if j==len2: 26 return i-j 27 else: 28 return -1