KMP算法-NFA版

首先是KMP算法的标准模板

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

int* buildNext(char* P){
    int m = strlen(P);
    int j = 0;
    int *N = new int[m]; // N[j]表示对于模式串的长度为j的前缀,能错位匹配的最大长度
                         // N[j]也可以解释为:如果第j个字符失配了,那么应该从N[j]开始继续尝试
    int t = N[0] = -1; // t是当前能错位匹配的最大长度
    while(j < m-1){
        if(t < 0 || P[j] == P[t]) // 如果下一位也能匹配
            N[++j] = ++t; // 记录,j移向下一位,t增大1
        else // 下一位不能匹配
            t = N[t]; // 窗口向右滑动
    }
    return N;
}

int kmp(char* T, char* P){
    int n = strlen(T);
    int m = strlen(P);
    int* N = buildNext(P);
    int i = 0, j = 0;
    while(i < n && j < m){
        if(j < 0 || T[i] == P[j])
            ++i, ++j;
        else
            j = N[j];
    }
}

int main()
{
    char s1[1000005], s2[1000005];
    cin >> s1 >> s2;
    cout << kmp(s1, s2) << endl;
    system("pause");
    return 0;
}

经过改进的KMP算法如下,改动在第9行。

int* buildNext(char* P){
    int m = strlen(P);
    int j = 0;
    int *N = new int[m]; // N[j]表示对于模式串的长度为j的前缀,能错位匹配的最大长度
                         // N[j]也可以解释为:如果第j个字符失配了,那么应该从N[j]开始继续尝试
    int t = N[0] = -1; // t是当前能错位匹配的最大长度
    while(j < m-1){
        if(t < 0 || P[j] == P[t]) // 如果下一位也能匹配
            ++j, ++t, N[j] = (P[j] != P[t] ? t : N[t]); // [little change here]
            // 这样理解:N[j]也可以解释为:如果第j个字符失配了,那么应该从N[j]开始继续尝试
            // 如果P[N[j]]与P[j]相同,一定会继续失败,与其等到时候再滑动窗口,不如现在就滑动,所以修改为N[j] = N[N[j]]
            // 根据递归的方法,如果0~j-1的N[]都弄成了最优的,那么当前也会是最优的
        else // 下一位不能匹配
            t = N[t]; // 窗口向右滑动
    }
    return N;
}

最后来一道KMP模板题。与上述代码不同的地方在于,本题要求输出所有的匹配位置,因此当j == m时不退出循环,而是继续滑动窗口(j = N[j]),当然,next数组的长度也要相应地从m变为m+1

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

int* buildNext(char* P){
    int m = strlen(P);
    int* N = new int[m+1];
    int j = 0;
    int t = N[0] = -1;
    while(j < m){
        if(t < 0 || P[t] == P[j])
            N[++j] = ++t;
        else
            t = N[t];
    }
    return N;
}

int main()
{
    char T[1000005], P[1000005];
    scanf("%s%s", T, P);
    int n = strlen(T);
    int m = strlen(P);
    int i = 0, j = 0;
    int* N = buildNext(P);
    while(i < n){
        if(j < 0 || T[i] == P[j])
            ++i, ++j;
        else
            j = N[j];
        if(j == m){
            printf("%d\n", i-j+1);
            j = N[j]; // 常规滑动窗口
        }
    }
    for(j = 1; j <= m; ++j)
        printf("%d ", N[j]);
    printf("\n");
    system("pause");
    return 0;
}

由于这道题的特殊要求,改进后的next数组无法满足,所以不采取优化版本

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章