算法练习-第九天【字符串】
字符串(KMP算法)
28. 找出字符串中第一个匹配项的下标
思考
一道简单的字符串单模匹配的题目。
使用暴力匹配,时间复杂度O(m*n)。
func strStr(haystack string, needle string) int {
m, n := len(haystack), len(needle)
outer:
for i := 0; i+n <= m; i++ {
for j := 0; j < n; j++ {
if haystack[i+j] != needle[j] {
continue outer
}
}
return i
}
return -1
}
进阶
KMP算法的主要思想是当出现字符不匹配时,可以知道当前已匹配的内容,可以利用这些信息避免重新匹配。
前缀表:是KMP算法中用来回退的,它记录了模式串与主串不匹配的时候,模式串应该从何处开始匹配。记录下标i(包括i)的字符串中,有多大长度相同的前后缀。
最长相等前后缀
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串
因为找到了最长相同前后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相等的前缀的后面开始匹配就可以了。
func strStr(haystack string, needle string) int {
// 构造next数组
next := make([]int, len(needle))
j := -1
next[0] = j
for i := 1; i < len(needle); i++ {
// 不相等 回退
for j >= 0 && needle[i] != needle[j+1] {
j = next[j]
}
if needle[i] == needle[j+1] {
j++
}
next[i] = j
}
// 匹配字符串
j = -1
for i := 0; i < len(haystack); i++ {
for j >= 0 && haystack[i] != needle[j+1] {
j = next[j]
}
if haystack[i] == needle[j+1] { // 匹配
j++
}
if j == len(needle)-1 {
return i - len(needle) + 1
}
}
return -1
}
总结
在KMP算法的next数组中, 可以使用前缀表统一减一的方式,也可以使用前缀表不处理的方式。
浙公网安备 33010602011771号