https://leetcode-cn.com/problems/implement-strstr/
func strStr(haystack string, needle string) int {
n:=len(haystack)
m:=len(needle)
if m == 0{
return 0
}
if n < m{
return -1
}
next, q := getNext(needle), 0
for i := 0; i < n; i++ {
for q > 0 && haystack[i] != needle[q]{
q=next[q-1]
}
if (haystack[i] == needle[q]){
q++
}
if (q == m){
return i+1-m;
}
}
return -1
}
- next数组是为了让我们在失去匹配的时候迅速跳转到相同前缀的下一字母,以便快速地进行连续比较。
- 从内层for循环看出,如果持续失配,q最终会回退到0(回到最初的起点)。
- next[n]存放着最长相同前缀(与某位到n位的字符串相同的最长字符串)的下一个位置。
- 因为q是发生匹配/失配的位置,所以最大相同前缀的位置应该是n = q-1,因此回退操作为q = next[q-1]。
如果理解了next的作用就好办了,获取next数组的过程其实是相同的逻辑:
//pattern(简称pat)
func getNext(pat string) []int {
next:=make([]int, len(pat))
k:=0
for q := 1; q < len(pat); q++ {
for k > 0 && pat[k] != pat[q]{
k=next[k-1];
}
if pat[k] == pat[q]{
k++;
}
next[q]=k;
}
return next
}
- 从
if pat[k] == pat[q]{ k++; } next[q]=k;
可以看出,持续匹配的情况下,代表q处和k处拥有目前最长的相同前缀,通过k++可以赋值让next[q]得到最长相同前缀的下一位。 - 当pat[k]与pat[q]发生失配的时候,通过next[k-1]回退找到与pat[k-1]有相同前缀字符串的下一位,再跟pat[q]对比。如果持续失配,最终会回退到0(回到最初的起点)。
总结
获取next数组:
指向目标串的下标q是不停前进的。
指向对比串的下标k是可以回退的。
获取next数组其实就是pat串和pat串匹配的过程,只不过指向目标串的下标要比对比串的下标先走一步(q=1, k=0)。循环过程会遇到两种情况:
1、失配:回退找最长相同前缀字符串的下一位继续匹配,持续失配将回退至起点为止。
2、匹配:k++,目的是为了让next[q]得到最长相同前缀的下一位。
每个循环,next[q]都可以得到最长相同前缀的下一位,或0。
使用next数组:
指向目标串haystack的下标i是不停前进的。
指向对比串needle的下标q是可以回退的。
使用next数组是haystack串和needle串匹配的过程,下标同时起步(i=0,q=0),循环过程会遇到两种情况:
1、失配:先需要知道最长相同前缀字符串的位置是在q-1(因为在q位失配了),再用next[q-1]跳转到最长相同前缀字符串的下一位,继续比较,持续失配将回退至起点为止。
2、匹配:q++,进入到下一位。
如果q等于needle串的长度,说明已经全部匹配完成了,这时候可以得到串的起始位置:i+1-m