【学习笔记】【字符串基础】KMP

发布时间 2023-07-18 15:23:09作者: Sonnety

你先别急咱也在学呢所以没更新完(

KMP

前言:暴力匹配算法

在学习KMP之前,我们首先要解决一个问题:

有两个字符串,一个是主串\(S\),一个是模式串\(P\)\((S.len>P.len)\),要求求出\(P\)\(S\)中的位置,不存在输出\(-1\).

看到这样的问题,先写一个暴力,时间复杂度统统不管,毕竟都暴力了要啥自行车

我们从第一位开始匹配:

\[ \begin{aligned} &S== B B C A B C D A B A B C D A B C D A B D E\\ &P== A B C D \end{aligned} \]

我们设暴力匹配时,\(S\)的枚举位置是\(i\)\(P\)的枚举位置是\(j\)

\(i==0,j==0\)

\(S\)的第一位是\(B\)\(P\)的第一位是\(A\),不匹配,移动\(i\)向前一位,\(j\)不变为0。

\[ \begin{aligned} S== B &B C A B C D A B A B C D A B C D A B D E\\ P=== &A B C D \end{aligned} \]

依旧不匹配,向前至匹配时候。

\[ \begin{aligned} S== B B C &A B C A B A B C D A B C D A B D E\\ P===== &A B C D \end{aligned} \]

现在\(i==3,j==0\)\(S_3==P_0==A\),匹配上了,\(++i,++j\)

\[ \begin{aligned} S== B B C A &B C A B A B C D A B C D A B D E\\ P===== A &B C D \end{aligned} \]

\(i==4,j==1,S_4==P_1==B\),还在匹配,还在匹配!!!!

\[ \begin{aligned} S== B B C A &B C A B A B C D A B C D A B D E\\ P===== A &B C D \end{aligned} \]

\(i==6,j==3,S_6==A,P_3==D\),最后一位没匹配上。。。怎么办?

如果没有匹配上,而且我们还匹配上了几步,肯定不能像之前一样直接\(++i\),必须从零开始,与此同时\(++i\)

操作应为\(i=i-j+1;j=0;\),下一次匹配是这样的:

\[ \begin{aligned} S== B B C A &B C A B A B C D A B C D A B D E\\ P====== &A B C D \end{aligned} \]

于是就有了代码:

暴力匹配代码
int ViolentMatch(char* s, char* p)  
{  
    int sLen = strlen(s);  
    int pLen = strlen(p);  
  
    int i = 0;  
    int j = 0;  
    while (i < sLen && j < pLen)  
    {  
        if (s[i] == p[j])  
        {  
            //①如果当前字符匹配成功(即S[i] == P[j]),则i++,j++      
            i++;  
            j++;  
        }  
        else  
        {  
            //②如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0      
            i = i - j + 1;  
            j = 0;  
        }  
    }  
    //匹配成功,返回模式串p在文本串s中的位置,否则返回-1  
    if (j == pLen)  
        return i - j;  
    else  
        return -1;  
}  

然后这个暴力显然是不够优雅的,我们看我们最后一个匹配例子:

\[ \begin{aligned} S== B B C A &B C A B A B C D A B C D A B D E\\ P====== &A B C D \end{aligned} \]

在之前的匹配中我们知道\(i==4,j==1,S_4==P_1==B\neq P_0==A\),i回溯是浪费我们的时间,就应该直接跳到\(i==7,S_7==A\)的正确位置。

1.正确位置在哪里?

这个正确位置,在\(KMP\)中是\(next\).

我们上面找的例子里面模式串\(P\)中没有重复的字符,没有代表性,所以我们重新整一个新的例子:

\[ \begin{aligned} &S==B B C A B C D A B A B C D A B D E\\ &P==A B C D A B D \end{aligned} \]

直接匹配到该回溯的位置:

\[ \begin{aligned} S==B B C &A B C D A B A B C D A B D E\\ P=====&A B C D A B D \end{aligned} \]

暴力匹配下一步应该是这样的:

\[ \begin{aligned} S==B B C A &B C D A B A B C D A B D E\\ P======&A B C D A B D \end{aligned} \]

但是我们希望是这样的:

\[ \begin{aligned} S==B B C A B C D &A B A B C D A B D E\\ P=========&A B C D A B D \end{aligned} \]

这样是不重不漏的,而且\(i\)也不会回溯。