KMP(golang)

80 阅读1分钟

KMP

序列aabaabaabksse,是否包含子序列aabaabk

经典算法

从左到右,每个字符当作开头,看是否匹配aabaabk

1.从0开始比对

image.png

2.当匹配到第六个字符k,发现不相等,主指针跳回0+1,子指针跳回0

image.png

2.从1开始比对,发现不相等跳到1+1

3.直到主指针从3开始,找到相等,返回3

image.png

KMP算法

aabaab的最大前缀是指找到一个最大的数n,从左往右取n个字符==从右往左取n个字符,n不能等于字符串长度

aab==aab ,n=3

记录子序列的aabaabk每个位置的最大前缀

next =[-1,0,1,0,1,2,3]

1.从0开始比对,当匹配到子序列aabaabk第六个字符k时不相等

image.png

2.经典算法中,主序列需要跳回到1的位置从头开始比对

这里拿到next[6]=3,主序列不动,子序列从第六个跳到next[6]的位置,继续比对

image.png

完成加速

3.当子指针遍历完时,退出循环,返回index=主-子

image.png

func getIndexOf(s, m string) int {
   if len(s) == 0 || len(m) == 0 || len(m) < 1 || len(s) < len(m) {
      return -1
   }
​
   str1 := strings.Split(s, "")
   str2 := strings.Split(m, "")
   i1 := 0
   i2 := 0
​
   var next []int = getNextArray(str2)
   for i1 < len(str1) && i2 < len(str2) {
      if str1[i1] == str2[i2] {
         i1++
         i2++
      } else {
         if i2 == 0 {
            i1++
         } else {
            i2 = next[i2]
         }
      }
   }
​
   if i2 == len(str2) {
      return i1 - i2
   } else {
      return -1
   }
}
​
func getNextArray(str []string) []int {
   if len(str) == 1 {
      return []int{-1}
   }
   next := make([]int, len(str))
   next[0] = -1
   next[1] = 0
   i := 2
   cn := 0
   for i < len(next) {
      if str[i-1] == str[cn] {
         cn++
         next[i] = cn
         i++
      } else if cn > 0 {
         cn = next[cn]
      } else {
         next[i] = 0
         i++
      }
   }
   return next
}