KMP-字符串

虽然我们可以根据板子题:兔子和兔子同样实现线性字符串匹配的速度。

但是也有更好的算法KMP,其更高效快捷。

先看看什么是KMP算法?

例如 S = ababccabab 与T =  abacabab

我们来看next数组 ,依次S[i]与T[i]进行比较,若S[i]!= T[i]

我们只需要读取next[i]数组下的内容;例如第一次 各个前5个字符 T= aba|c|a 与S = aba|b|c中  abaca与ababc的aba匹配 但是c与b不匹配了

我们可以读取next[4] == 1, 即c下的数组;

往前匹配T[next[4]] (即T[1])是否等于 S[4] ;    很好,刚好相等;

我们继续比较接下来从T[2]开始与S[5]开始比较即可,前两位可以不再进行匹配;

如何创建NEXT数组:

如下所示:

其中(1) 为 S[I] == S[J]的情况

其中(2)就是回溯,其实本质就是,因为S[I] == S[J] 当前情况下b!=c,所以我们开始回溯,往回跑。即判断s[next[i-1]]是否等于s[i],若不是,继续回溯。

其实可以理解,当前的前缀是大括号,那么同样存在前缀中有前缀和后缀的关系,这样一直回溯。知道找到s[j = next[j-1]]==s[i]的情况 。

其中(3)前缀串--相对(2)操作的基础情况下的

附上代码

#include <iostream>
#include <string>
using namespace std;

const int N = 1e7;
int next_[N];

void get_next(string T) {
    int j = 0;
    next_[0] = 0;
    for (int i = 1; i < T.size(); i++) {
        while (j > 0 && T[i] != T[j]) {
            j = next_[j - 1]; // 回溯,寻找上一个的下一个点是否为T[i]
        }
        if (T[i] == T[j]) {
            j++;
        }
        next_[i] = j;
    }
    for (int i = 0; i < T.size(); i++) {
        cout << next_[i] << " ";
    }
    cout << endl;
}

int KMP(string S, string T) {
    int m = S.size();
    int n = T.size();
    int j = 0;
    for (int i = 0; i < m; i++) {
        while (j > 0 && S[i] != T[j]) {
            j = next_[j - 1]; // 回溯
        }
        if (S[i] == T[j]) {
            j++;
        }
        if (j == n) {
            int ans = i - n + 1;
            return ans;
        }
    }
}

int main() {
    string text = "abacabacacabcababccabab";
    string pattern = "ababccabab";

    // 打印文本和模式串
    cout << "Text: ";
    for (char c : text) {
        cout << c << " ";
    }
    cout << endl;

    cout << "Pattern: ";
    for (char c : pattern) {
        cout << c << " ";
    }
    cout << endl;

    // 计算并打印部分匹配表
    cout << "Next array: ";
    get_next(pattern);

    // 执行KMP算法进行匹配
    cout<<"res="<<KMP(text, pattern);

    return 0;
}
    
posted @   安娜アンナ  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示