KMP template matching algorithm - six steps to get KMP

KMP algorithm - six steps to get KMP

1. What is KMP

KMP algorithm is DEKnuth, J, H, Morris and co-sponsored VRPratt three chiefs, called Knuth-Morria-Pratt algorithm, referred to as KMP template matching algorithm. The algorithm with respect Brute-Force (violence) algorithm has a relatively large improvement, mainly eliminating the main string pointer back to improve time efficiency. (Space for time)

2.KMP and simple template matching (Brute-Force)

Violence Brute-Force

This comparison is super simple stepping through. . . .
Given a string S, T ,
a T to S first one of length T as the comparison string, returns a successful first index matching
is unsuccessful, continues to match the S next of length T the same word string
...... through each repeat length of the T same string, and matching
this time complexity is O (n * m).
Here Insert Picture Description
Here Insert Picture Description

KMP template matching algorithm - can implementation complexity O (m + n)

The first step: to understand what strings are prefixes and suffixes
Here Insert Picture Description such as:
abcjkdabc, then the longest prefix of the array and the longest suffix same - must be abc.
cbcbc, the same longest prefix and suffix longest - is cbc.
abcbc, longest prefix and suffix of the same maximum, it does not exist.
Note longest prefix: the first character is said to start, but do not include the last character.

第二步,理解关于字符串匹配的规则
假设S与T要进行匹配,匹配规则如图
Here Insert Picture Description
这里aba就是最长前缀字串,然后下一次a是最长前缀字串
Here Insert Picture Description
借别人的图示意:

Here Insert Picture Description

第三步,如何得出j的更新规则——即计算next数组
我看了数据结构(java)第四版,讲的很好。
公式:Here Insert Picture Description
例子演示:
Here Insert Picture Description
第四步,上计算next数组的代码

int get_next(string t,int * next) {
    //先定义一个数组
    int length = t.length();
    int j=0,k=-1;
    next[0] = -1;
    while(j<length-1)//因为我们是在找最长前缀字串,这里得减一。
        if(k==-1||t[j]==t[k]) { //当前的字串最新的字符相等或者字串总长度1
            k++;
            j++;//比较下一个字符
            next[j] = k;//最长前缀字串记录下来,因为前面的k,j都加了1

        } else
            k = next[k];//重新在k长度的前缀字串找当前j长度字串的最长前缀字串,更新索,听说这里是最难理解的
    return 1;
}

第五步,理解 k = next[k]
k = next[k];//重新在k长度的前缀字串找当前j长度字串的最长前缀字串,
这里我给例子演示,反正我是一下子就理解了。
Here Insert Picture Description
图示:因为p5!=p11,这个时候,最长前缀字串肯定不会比上次的大了,不可能是6,我们只能重新寻找,而重新寻找的规则就是k = next[k]=2
Here Insert Picture Description
第六步,最后一步,上匹配字串的代码

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
//建立next数组
int get_next(string t,int * next) {
    //先定义一个数组
    int length = t.length();
    int j=0,k=-1;
    next[0] = -1;//这里是为了
    while(j<length-1)//因为我们是在找最长前缀字串,这里得减一。
        if(k==-1||t[j]==t[k]) { //当前的字串最新的字符相等或者字串总长度1
            k++;
            j++;//比较下一个字符
            next[j] = k;//最长前缀字串记录下来

        } else
            k = next[k];//重新在k长度的前缀字串找当前j长度字串的最长前缀字串,更新索
    return 1;
}
int compare_str(string s,string t,int pos) {
    int n=s.length();
    int m = t.length();
    if(n<m||pos>=n||n==0) { //不合理的输入
        return 0;
    }
    if(pos<=0) {
        pos=0;//提高容错率
    }
    int next[m];
    //生成next数组
    get_next(t,next);
    int i=pos,j=0;
    while(i<n&&j<m)
        if(j==-1||s[i]==t[j]) { //当前匹配成功,继续匹配下一个字符
            i++;
            j++;
        } else { //i是不回溯,匹配规则更新匹配的j
            j = next[j];//这里就是我们说的匹配规则
            if(n-i+1<m-j+1)//剩下的字串不够t的长度,就不用比较了
                break;
        }

    if(j==m)
        return i-j;//成功匹配。,返回对应的索引
    return -1;//否则退出

}
int main() {
    string t,s;
    cin>>s;
    cin>>t;
    int pos =compare_str(s,t,0);
    cout<<pos;
    return 0;
}

更新改进KMP算法。

Here Insert Picture Description
现在我们比较的是C和B,不匹配。
显然,当我们上边的算法得到的next数组应该是[ -1,0,0,1 ]
所以next[3] = 1。下一步的匹配:明显我们又来比较C和B,这是是多余的
所以我们应该改进计算next数组匹配
Here Insert Picture Description
改进的算法 就是避免这种情况,代码演示

int get_next(string t,int * next) {
    //先定义一个数组
    int length = t.length();
    int j=0,k=-1;
    next[0] = -1;
    while(j<length-1)//因为我们是在找最长前缀字串,这里得减一。
        if(k==-1||t[j]==t[k]) { //当前的字串最新的字符相等或者字串总长度1
            k++;
            j++;//比较下一个字符
            //使用改进的算法
            if(t[j]!=t[k])
                next[j] = k;//最长前缀字串记录下来
            else next[j] = next[k];//直接跳到上次找到的后缀字串,改进的代码结束

        } else
            k = next[k];//重新在next[k]长度的前缀字串找当前j长度字串的最长前缀字串,更新索引
    return 1;
}

Here Insert Picture Description
Reference blog
https://blog.csdn.net/dark_cy/article/details/88698736
:
https://blog.csdn.net/starstar1992/article/details/54913261?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source= distribute.pc_relevant.none-task

Published 50 original articles · won praise 24 · views 2358

Guess you like

Origin blog.csdn.net/qq_44861675/article/details/104532051