Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b923ba5

Browse files
committed
kmp algorithm
1 parent 3147f85 commit b923ba5

File tree

1 file changed

+162
-3
lines changed

1 file changed

+162
-3
lines changed

028._implement_strstr().md

Lines changed: 162 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,34 @@
1010
Easy
1111

1212

13-
这个题目其实可以引来一大类,那就是关于string的算法,但是此处先用暴力算法来AC,然后再来细读/品味别的string相关算法吧。
13+
先来写最符合直觉的写法:
1414

15-
虽然是暴力算法,但是也不容易写对啊
15+
```
16+
class Solution(object):
17+
def strStr(self, haystack, needle):
18+
"""
19+
:type haystack: str
20+
:type needle: str
21+
:rtype: int
22+
"""
23+
i , j = 0, 0
24+
while i < len(haystack) and j < len(needle):
25+
if haystack[i] == needle[j]:
26+
i += 1
27+
j += 1
28+
else:
29+
i = i - j + 1 //退回j步,但是是不匹配的,我们就从下一位开始
30+
j = 0 //j置为0
31+
32+
if j == len(needle):
33+
return i - j
34+
else:
35+
return -1
36+
```
1637

1738

39+
暴力解法二:
40+
1841
```
1942
class Solution(object):
2043
def strStr(self, haystack, needle):
@@ -33,4 +56,140 @@ class Solution(object):
3356
if j == len(needle):
3457
return i
3558
return -1
36-
```
59+
```
60+
61+
62+
63+
64+
参考[(原创)详解KMP算法](http://www.cnblogs.com/yjiyjige/p/3263858.html)
65+
66+
以及 [KMP算法的Next数组详解](http://www.cnblogs.com/tangzhengyue/p/4315393.html)
67+
68+
> 利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置
69+
70+
71+
> 至此我们可以大概看出一点端倪,当匹配失败时,j要移动的下一个位置k。存在着这样的性质:最前面的k个字符和j之前的最后k个字符是一样的。
72+
73+
> 如果用数学公式来表示是这样的
74+
75+
> P[0 ~ k-1] == P[j-k ~ j-1]
76+
77+
78+
> 简单的证明:
79+
80+
> 因为:
81+
82+
> 当T[i] != P[j]
83+
84+
> 有T[i-j ~ i-1] == P[0 ~ j-1]
85+
86+
> 由P[0 ~ k-1] == P[j-k ~ j-1]
87+
88+
> 必然:T[i-k ~ i-1] == P[0 ~ k-1]
89+
90+
好像有点理解了,比如 P 的前j个字符和 T 都匹配(匹配到i):
91+
92+
- P 中没有重复出现的字符(abcdex),那么很直觉的,我们知道不需要移动i,然后需要把 P 的j移动到0,再次开始对比。
93+
94+
- P 中有重复出现的字符(abcabx),那么也还是比较直觉的,因为 P 与 T 已经匹配上,所以说明 T 中前面也有重复出现的字符,那么我们也可以跳过重复字符的比较部分,拿着新的字符来比较。
95+
96+
看这两个例子:
97+
98+
99+
```
100+
i
101+
|a|b|c|d|e|f|g|a|b|... T
102+
|a|b|c|d|e|x| P
103+
j
104+
105+
i
106+
|a|b|c|d|e|f|g|a|b|... T
107+
|a|b|c|d|e|x| P
108+
j
109+
```
110+
111+
112+
这个是没有重复的例子,那么还是比较贴近我们直接的,下次比较我们会直接从 j = 0 再次开始。
113+
114+
115+
```
116+
i
117+
|a|b|c|a|b|a|a|b|c|... T
118+
|a|b|c|a|b|x| P
119+
j
120+
121+
i
122+
|a|b|c|a|b|a|a|b|c|... T
123+
|a|b|c|a|b|x| P
124+
j
125+
```
126+
127+
128+
这里有重复,同时我们也知道前面的重复部分已经匹配, 所以我们调到匹配失败但是重复开始的间隔点。
129+
130+
这样也和文中给的例子匹配起来了:
131+
132+
```
133+
0 k j-k j
134+
| a | b | c | a | b | b
135+
k-1 j-1
136+
```
137+
138+
这个k就是每次k需要跳回的部分。
139+
140+
141+
然后到了算法部分:
142+
143+
> 好,接下来就是重点了,怎么求这个(这些)k呢?因为在P的每一个位置都可能发生不匹配,也就是说我们要计算每一个位置j对应的k,所以用一个数组next来保存,next[j] = k,表示当T[i] != P[j]时,j指针的下一个位置。
144+
145+
146+
虽然还是有点迷茫(关于next数组的部分),但是KMP AC代码如下:
147+
148+
149+
```
150+
class Solution(object):
151+
def strStr(self, haystack, needle):
152+
"""
153+
:type haystack: str
154+
:type needle: str
155+
:rtype: int
156+
"""
157+
def getNext(p):
158+
next = [0 for _ in range(len(p))]
159+
next[0] = -1
160+
j = 0
161+
k = -1
162+
while j < len(p) - 1:
163+
if k == -1 or p[j] == p[k]:
164+
j += 1
165+
k += 1
166+
next[j] = k
167+
else:
168+
k = next[k]
169+
170+
return next
171+
172+
173+
def kmp(t, p):
174+
i = 0
175+
j = 0
176+
next = getNext(p)
177+
while i < len(t) and j < len(p) :
178+
if j == -1 or t[i] == p[j]: # 当j为-1时,要移动的是i,当然j也要归0
179+
i += 1
180+
j += 1
181+
else:
182+
# i 不需要回溯了
183+
# i = i - j + 1
184+
j = next[j] # j回到指定位置
185+
186+
if j == len(p):
187+
return i - j
188+
else:
189+
return -1
190+
191+
if needle:
192+
return kmp(haystack, needle)
193+
else:
194+
return 0
195+
```

0 commit comments

Comments
 (0)