【KMP算法】思路,理解,C++,Python

本文深入探讨KMP算法,解析其工作原理与实现步骤。通过对比S串与P串的匹配过程,阐述如何利用预处理避免重复比较,提高字符串搜索效率。文章提供了C++与Python代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

是不是每个程序员都要在人生的某个时刻把KMP算法彻底搞懂?之前写过几次KMP了,每次都是现学现写,感觉没有真正掌握,这次写一篇文章整理总结一下,希望下次能直接写出来。这是我学习过程中看的几篇文章,感谢博主们。但是之前有次还看过一篇更透彻的……忘了是不是在优快云了,找不到了。

https://blog.youkuaiyun.com/Heathy__/article/details/60875067

https://blog.youkuaiyun.com/v_july_v/article/details/7041827

主要过程:

(P[a,b]表示包含P[a]不包含P[b]的子串)

1、现在我们想知道P串是否是S串的子串。我们从头开始,依次检查P串的每一位,和S串的每一位,现在我们检查到P[j] 和S[i],即P[0:j]==S[i-j:i],当前有两种可能:

①P[j]==S[i],继续检查下一位;

②P[j]!=S[i]:这说明从S[i-j]这一位开始的子串!=P, 一般来说,我们下一步是检查S[i-j+1]开始的子串能不能==P,也就是P要从头检查了,而S也好像前功尽弃,要退回到S[i-j+1]位置重新检查。

然而 ∵P[0:j]==S[i-j:i]  ∴P[j-k:j]==S[i-k:i]

  如果 P[0:k]==P[j-k:j],那么 P[0:k]==S[i-k:i], 也就是说如果我们能提前知道一些P串的信息,即求得k,就相当于P[0:k]的检查已经完成,下一步检查P[k]和S[i]的对应情况。这样,对S串的扫描是一次的,对P串,我们需要知道P[j]位不对应时的k。

2、下面我们对P串预处理,用一个next数组存储k,即next[j]=k。这一步不需要S串。

P[0:next[j]]==P[j-next[j]:j]

①如果P[j]==P[next[j]],那么P[0:next[j]+1]==P[j+1-next[j-1]-1:j+1], 即 next[j+1]==next[j]+1

②如果P[j]!=P[next[j]],请注意红色的式子包含了很多信息,比如令s=next[j](s有意义) P[0:s]==P[j-s:j] 那么P[0:next[s]]==P[j-next[s]:j] (把上面的绿式子,S换成P,i换成j,就能得到) 现在我们得到了一个和红式子长得很像的蓝式子,我们又要开始讨论:

                                ①如果P[j]==P[next[s]],那么……,next[j+1]==next[s]+1

                                ②如果P[j]!=P[next[s]], ……

……

够幸运的话,我们很快就能算出next[j+1];或者,我们会发现没法再用令a=next[b]这个代换了(我们上面用了s=next[j]),也就是a是负数或者0的情况(P[0:0]是个空串),这说明绿式子第二行的“如果”假设不成立,我们没法找到一个k来简化过程。以k==0为例:就相当于P[0:0]的检查已经完成,下一步检查P[0]和S[i]的对应情况。所以next[j+1]=0

以上说明可能不太形象,可配合画图食用。但是我个人感觉画图虽然理解容易,但是不如数学语言更容易转化成代码。

(实际上写的是next[j] = k-1,为了好写

C++:

#include <iostream>

using namespace std;
int nxt[1000];
//char S[1000] = "ababcababcababababcabaabc";
char S[1000] = "ababcababacbabababcabcab";
char P[1000] = "ababcabaa";

int main()
{
    int k=0, j = 1;
    nxt[0] = -1;
    while(P[j]){
        if(P[j]==P[nxt[k]+1]) {
            nxt[j] = nxt[k]+1;
            k = j++;
        }
        else {
            k = nxt[k];
            if(k<0){
                nxt[j] = -1;
                k = j++;
            }
        }
    }

    /*j = 0;
    while(P[j]){
        cout<<nxt[j++]<<' ';
    }*/

    int i = 0;
    bool flag = 0;
    j = 0;
    while(S[i]){
        if(S[i]==P[j]){
            i ++;
            j ++;
            if(!P[j]) {flag = 1; break;}
        }
        else{
            if(j==0) i ++;
            else j = nxt[j]+1;
        }
    }

    if(flag) cout<<"Y";
    else cout<<"N";

    return 0;
}

Python:

nxt = {0:-1}
#S = "ababcababcababababcabaabc"
S = "ababcababacbabababcabcab"
P = "ababcabaa"

k = 0
j = 1
while j<len(P):
    if P[j]==P[nxt[k]+1]:
        nxt[j] = nxt[k]+1
        k = j
        j += 1
    else:
        k = nxt[k]
        if k<0:
            nxt[j] = -1
            k = j
            j += 1

i = j = 0
flag = 0
while i<len(S):
    if S[i]==P[j]:
        i += 1
        j += 1
        if j>=len(P):
            flag = 1
            break
    else:
        if j==0: i += 1
        else: j = nxt[j]+1



if flag: print "Y"
else: print "N"

(Python差不多就是翻译的C++,Python太久不看了都忘了,正在慢慢温习。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值