串匹配--KMP算法

next数组

首先我们先要了解一下什么是字符串的前缀和后缀

image-20200818125422945.png

前缀

字符串的前缀是指字符串的任意首部。比如:望,望江,望江楼,望江楼上.......

后缀

字符串的任意尾部是字符串的后缀。比如:流,江流,江江流,望江江流......

前缀和后缀的公共子串

字符串前缀和后缀相同的字串。比如:“望江楼上望” 中的 字串“望”, “望江楼上望江”中的“望江”

图解求next数组

第一个字符前没有字符串,对应数组的值为-1(默认的)

image-20200818130931277.png

第二个字符前的字符串,没有公共子串(从这里开始,next数组对应的值是.公共子串的长度)

image-20200818131315754.png

第三个字符前的字符串,没有公共子串

image-20200818131457941.png

第四个字符前的字符串,没有公共子串

image-20200818131630033.png

第五个字符前的字符串,没有公共子串

image-20200818131751669.png

第六个字符前的字符串,出现了公共子串“望”,长度是1,对应数组的值:1

image-20200818131945204.png

第七个字符前的字符串,出现了公共子串“望江”,长度是2,对应数组的值:2

image-20200818132105327.png

第八个字符前的字符串,没有公共子串

image-20200818143527061.png

代码实现

package main

import "fmt"

func main() {
    var str string = "望江楼上望江江流"

    text := []rune(str)
    next := next(text)

    for _, i2 := range next {
        fmt.Printf("%d,", i2)
    }
}

func next(str []rune) []int {

    strLength := len(str)
    next := make([]int, strLength, strLength)
    next[0] = -1

    k := -1 //记录公共子串的长度
    j := 0

    for j < strLength-1 {
        if k == -1 || str[k] == str[j] {
            k++
            j++
            next[j] = k
        } else {
            k = next[k]
        }
    }

    return next

}

运行结果:-1,0,0,0,0,1,2,0,

kmp算法

KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,如果next[j]>=0,则目标串的指针i不变,将模式串的指针j移动到next[j]的位置继续进行匹配;若next[j]=-1,则将i右移1位,并将j置0,继续进行比较。

图解

第一次匹配

第四个字符失配。字串的初始值为对应的第四个字符的next数组中的值 0。

image-20200818143819897.png

第二次匹配

由于前三个是匹配的,而第四个字符失配,没有公共字串,那么无需再比较前三个字符。

这里的长串索引没有初始化,还是上次失配的值,就是3

短串初始值是0

image-20200818144237660.png

第三次匹配

第三个字符失配,短串初始值为对应的next数组中的0

image-20200818144543173.png

第四次匹配

第一个字符失配(就将长串索引+1,字串索引+1,代码中会有一个判断)

image-20200818144659318.png

第五次匹配

第一个字符失配(就将长串索引+1,字串索引+1,代码中会有一个判断)

image-20200818144740589.png

第六次匹配

第七个字符失配。出现了公共字串“望江”,下次字串的索引初始值是对应的2。

image-20200818145348408.png

第七次匹配

长串的索引值还是上次失配的地方的值:14

短串初始值:2

image-20200818145703011.png

后面的就省略了

........

代码

package main

import "fmt"

func main() {
    var shortStr string = "望江楼上望江江流"
    var longStr string = "望江楼,望江流,望江楼上望江流,江楼千古,江流千古"
    shortText := []rune(shortStr)
    longText := []rune(longStr)

    result := kmp(shortText, longText)

    if result == -1 {
        fmt.Println("没有匹配到")
    }else {
        fmt.Println("匹配到首次出现的索引位置是:", result)
    }

}

func kmp(shortStr, longStr []rune) int{

    shortStrLen := len(shortStr)
    longStrLen := len(longStr)

    if shortStrLen > longStrLen {
        return -1
    }

    var i int = 0 //长串索引
    var j int = 0 //短串索引

    next := next(shortStr)

    for i < longStrLen {
        if j== -1 || longStr[i] == shortStr[j] {
            i++;
            j++;
        }else {
            j = next[j];
        }

        if j == shortStrLen {
            return i - j
        }

    }
    return -1
}

func next(str []rune) []int {

    strLength := len(str)
    next := make([]int, strLength, strLength)
    next[0] = -1

    k := -1 //记录公共子串的长度
    j := 0

    for j < strLength-1 {
        if k == -1 || str[k] == str[j] {
            k++
            j++
            next[j] = k
        } else {
            k = next[k]
        }
    }

    return next
}

运行结果:没有匹配到

Last modification:August 21st, 2021 at 06:15 pm
如果觉得我的文章对你有用,请随意赞赏