KMP算法详细图解

69 阅读3分钟

KMP算法字符串匹配算法:

KMP算法匹配图解

如下图p在t中找到可以匹配的下标: Screenshot_20240510_210016_com.newskyer.draw.jpg 常规:我们常规做法都是暴力匹配,此处我们不介绍暴力匹配。 KMP的做法:

Screenshot_20240511_134800_com.newskyer.draw.jpg 此时下标为5处发生不匹配,首先我们先介绍KMP如何进行匹配,稍后在介绍next怎么得到的

Screenshot_20240511_151757_com.newskyer.draw.jpg

这时,看到我们将p下标为3的位置再和T下标为5的位置进行匹配,也就是T下标为5处不匹配时,需要看next数组前一个索引(4)处的元素是多少,可见next[4]的元素是3 则P下标为3元素与T下标为5的元素进行匹配。接着在看到T的下标为7的位置又发生了不匹配现象

Screenshot_20240511_153005_com.newskyer.draw.jpg

Screenshot_20240511_153152_com.newskyer.draw.jpg 此时你大概知道KMP的算法流程了。

我们再来看最后一个例子:

Screenshot_20240510_214833_com.newskyer.draw.jpg 此时看到下标为4的地方发生匹配错误,根据P下标4前一个(3)元素对应的next元素为1,则将P数组下标为1处与T的下标为4处进行匹配。

Screenshot_20240511_154026_com.newskyer.draw.jpg

此时发现T的索引为5的位置,发生了不匹配现象.

Screenshot_20240511_154509_com.newskyer.draw.jpg

此时,我们看T索引5对应匹配数组P的索引为2,则我们看2的前一个位置P数组索引为1,对应的next的元素是多少,此时发现元素为0,为0时,我们的P数组不对T下标为5处的位置进行匹配,数组P之间后移从T的索引为6处进行匹配。

Screenshot_20240511_154840_com.newskyer.draw.jpg

最终匹配完成。,总结一下匹配过程,若T的i处和P的j处发生不匹配,接下来我们看P的j-1处的next元素是多少,next[j-1]处的元素,为P的匹配索引,再跟T的i处进行匹配,若还是不匹配,则再接着回跳,也就是再看 P前一个索引的next元素。

接下来我们介绍一下 P的next数组是怎么来的:

Screenshot_20240511_213926_com.newskyer.draw.jpg 如图next数组是这样得到的。 Screenshot_20240511_223600_com.newskyer.draw.jpg

我们来模拟一下next数组的生成

Screenshot_20240511_221144_com.newskyer.draw.jpg

#include<iostream>
using namespace std;

const int N  = 1e5 + 10, M = 1e6 + 10;

int n, m;
char p[N], s[M];
int ne[N]; // next数组


//next数组的生成
void getNext() {
    for(int i = 2, j = 0; i <= n; i++) {
        /*
        	i代表 前缀子串末尾, j代表后缀字串的末尾,也代表 next数组的元素

        	前后缀不相同时,我们此时根据前一个next[j],来找前一个前缀子串,进行比较,
        		也就是 j进行回退的过程,直到 j ==0 或者 p[i] == p[j+1]; 退出 j的回退
        */
        while(j > 0 && p[i] != p[j + 1]) j = ne[j];
        /*
        	前后缀相等时,j向后移动一位
        */
        if(p[i] == p[j + 1]) j++;
        ne[i] = j;
    }
}


int main() {
    cin >> n >> p + 1 >> m >> s + 1;
    getNext();
    //KMP 的匹配过程
    for(int i = 2, j = 0; i <= m; i++) {
        while(j > 0 && s[i] != p[j + 1]) j = ne[j];
        if(s[i] == p[j + 1]) j++;
        if(j == n) { // j == n则找到 匹配的子串
            cout << i - n + 1 << '\n';
        }
    }
    // next数组的值;
    for(int i = 1; i <= n; i++) {
        std::cout << ne[i] << " ";
    }
    std::cout << '\n';

    return 0;
}

如果对你有帮助请点赞收藏谢谢!