(一)KMP算法的DFA解法

简单说一下,KMP是一个时间复杂度O(n+m)的字符串匹配算法,网上也有很多其他的解法,比如next数组的解法,推荐看这里

匹配的思路

DFA:确定有限自动状态机
以下是ABABAC的对应的DFA:
《(一)KMP算法的DFA解法》

1.假设文本为txt,待匹配的子串为pat。

2.未匹配是状态0,此时如果条件A(匹配到了txt的字符‘A‘),会到状态1,如果是条件B、C或其他的条件,还是状态0。如果经历了匹配ABABA之后到达状态5,再匹配C到达状态6,即停止状态,也就是匹配成功。

3.很明显,dfa[][]是一个二维数组,dfa['A'][3]就是在状态3时,下一个条件是A时,要跳转到哪一个状态。

4.匹配的过程就是:不断的用txt的字符 + 当时的状态去dfa取值,直到状态6 or txt匹配到最后一个字符,结束。

5.匹配过程中,txt的字符是一个一个往后取,不会回溯,只是状态一直在变化,即本次使用的是上一次匹配后的状态,不会回溯,正是KMP算法相对于暴力解法的优势。

DFA数组的构建

上边的思路如果理解了,那么剩下的问题就是如何构建dfa[][],当然这也是难点。dfa[][]的元素分为两类:1,匹配成功;2,匹配失败。

1.匹配成功
初始可确定的状态为:
dfa[pat[0]][0] = 1;
即在状态0匹配到子串的第一个字符,到达状态1;
dfa[pat[1]][1] = dfa[pat[0]][0] + 1

2.匹配失败
初始的第一列,即状态0时,匹配失败应该还是状态0,也就是除上述dfa[pat[0]][0] = 1外,其他都为状态0。
那么状态j时,匹配失败:(模拟回溯)
假设此时匹配到c = txt[i],然后查找dfa找到跳转到哪个状态,那么txt[i-j…i-1]这部分与pat[0…j-1]这部分是匹配的。因为i-j为起始而整体不匹配,所以pat要右移一位,所以txt[i-j]舍弃了,不用比较了,也就是在状态j遇到条件c的要跳转的状态,txt[i-j+1 … i-1] == pat[1 … j-1]。
引入一个状态指示数组X,X[j]是指正确输入p[1…j-1]后进入的状态,输入p[1…j-1]c后进入的状态就是dfa[c][X[j]](在X[j]状态时输入c),即dfa[c][j]=dfa[c][x[j]]。
X[j+1]为正确输入p[1…j]后进入的状态,即正确输入p[1…j-1]p[j]后进入的状态,也就是在X[j]状态时输入p[j]时进入的状态,就是dfa[pat.charAt[j]][X[j]],即递推公式为:X[j+1]=dfa[pat.charAt[j]][X[j]],而X[0]手动初始化为0。
故有:

dfa[pat[0]][0] = 1;
 int X = 0;
 for (j = 1; j < M; ++j){
 for(i = 0; i < R; ++i) {
 dfa[i][j] = dfa[i][X];
 }
 dfa[pat[j]][j] = j + 1; //匹配成功
 X = dfa[pat[j]][X]; //更新重启状态
 }

附所有代码:

int test_jp_search() {
    int index = jp_search("BCBAABACAABABACAA", "ABABAC");
    printf("%d\n", index);
    return 0;
}

int jp_search(char *txt, char *sub)
{
    int M = (int)strlen(sub);
    int R = 256;
    //二维数组定义并初始化
    int dfa[R][M];
    int i = 0, j = 0;
    for (i = 0; i < R; ++i) {
        for (int j = 0; j < M; ++j) {
            dfa[i][j] = 0;
        }
    }

    dfa[sub[0]][0] = 1;
    int X = 0;
    for (j = 1; j < M; ++j){
        for(i = 0; i < R; ++i) {
            dfa[i][j] = dfa[i][X];
        }
        dfa[sub[j]][j] = j + 1; //匹配成功
        X = dfa[sub[j]][X]; //更新重启状态
    }


    int N = (int)strlen(txt);
    for (i = 0, j = 0; i < N && j < M; ++i){
        j = dfa[txt[i]][j];
    }

    if (j == M) {
        return i - M;
    } else {
        return M;
    }
}

ps:感觉还是有点乱,回头再整理一下思路

参考:
1.《算法》第四版
2.链接

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/u013679882/article/details/63807324
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞