KMP算法(供个人使用)

本文深入讲解了KMP算法的工作原理及实现方式,介绍了如何通过计算next数组来避免回溯,提高字符串匹配效率。

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

KMP算法

KMP算法,用于查找 模式串在主串中是否出现的一种快速的,不会溯指针的算法。
一般的思路查找模式串在主串中是否出这里写代码片现的算法过于复杂(还未学数据结构暂时不会计算复杂度)
以下代码 源自kuangbin的 ACM模板,仅供学习理解使用。

KMP算法思想:

当比较模式串和主串之间的是否相同时,肯定从第一个字符开始比较,如若相同,则查找下一个。
当出现不相同时,我们已知前几个字符已经比较过,如果他们和前几个字符相同时,可以直接从模式串的该位置向后比较,而它们和前几个字符都不同时,可以直接从第一个比起,这样指针就不用回溯了;

所以前移k位之后,可以继续比较位置i的前提是f的前i-1个位置满足:长度为i-k-1的前缀A和后缀B相同。只有这样,我们才可以前移k位后从新的位置继续比较。

这里写图片描述

所以kmp算法的核心即是计算字符串f每一个位置之前的字符串的前缀和后缀公共部分的最大长度(不包括字符串本身,否则最大长度始终是字符串本身)。获得f每一个位置的最大公共长度之后,就可以利用该最大公共长度快速和字符串O比较。当每次比较到两个字符串的字符不同时,我们就可以根据最大公共长度将字符串f向前移动(已匹配长度-最大公共长度)位,接着继续比较下一个位置。事实上,字符串f的前移只是概念上的前移,只要我们在比较的时候从最大公共长度之后比较f和O即可达到字符串f前移的目的。

这里写图片描述

next数组计算

理解了kmp算法的基本原理,下一步就是要获得字符串f每一个位置的最大公共长度。这个最大公共长度在算法导论里面被记为next数组。在这里要注意一点,next数组表示的是长度,下标从1开始;但是在遍历原字符串时,下标还是从0开始。假设我们现在已经求得next[1]、next[2]、……next[i],分别表示长度为1到i的字符串的前缀和后缀最大公共长度,现在要求next[i+1]。由上图我们可以看到,如果位置i和位置next[i]处的两个字符相同(下标从零开始),则next[i+1]等于next[i]加1。如果两个位置的字符不相同,我们可以将长度为next[i]的字符串继续分割,获得其最大公共长度next[next[i]],然后再和位置i的字符比较。这是因为长度为next[i]前缀和后缀都可以分割成上部的构造,如果位置next[next[i]]和位置i的字符相同,则next[i+1]就等于next[next[i]]加1。如果不相等,就可以继续分割长度为next[next[i]]的字符串,直到字符串长度为0为止。

以下为模式串的next的一个例子:

这里写图片描述

KMP算法

以下为邝斌代码:

(注:邝斌将第一个数的next值设置为-1)

#include
#include
int Next[10010];

void kmp_pre (char x[],int m,int next1[])  //算Next的值 亦为KMP的 准备阶段
{
    int i,j;
    j=next1[0]=-1; //第一个字符的next置为-1
    i=0;
    while(i
    {
        while(j!=-1 && x[i]!=x[j]) //当j的值不为-1 且 字串与的这个字符与
        {
            j=next1[j];// j置为与其下标对应的next值(下面例子前面 b到都先置为-1 下一步注释处加到0)
        }
        next1[++i]=++j; //i先加1  ,j再加1 next[i] 的值为j的值
    }
}

**//一个字符串及其对应的 next值(邝斌版)
a b c a b c x x x x a b c a b b o o o o o a b c a b x x x
-1 0 0 0 1 2 3 0 0 0 0 1 2 3 4 5 0 0 0 0 0 0 1 2 3 4 5 0 0**

不难推出后面每个字符都是前一个字符与前几个字符的重复数目;
这里写图片描述
KMP算法

void preKMP(char x[],int m,int kmpNext[])

{

int i,j;

j=kmpNext[0]=-1;

i=0;

while(i

{

while(-1!=j && x[i]!=x[j])j=kmpNext[j];

if(x[++i]==x[++j])kmpNext[i]=kmpNext[j];

else kmpNext[i]=j;

}

}




int KMP_count (char x[],int m,char y[],int n) //计数器,计算母串中一共有多少的字串;
{//X是模式串 Y是主串
    int i,j;
    int ans=0;//计数,记录主串中有多少与模式串中相同的段;
    kmp_pre(x,m,Next);
    i=j=0;
    while(i因为数组从x[0]开始计数所以保证从零开始,0即是第一个数所以从零开始判断相当于从字符串的第一个数开始判断;
    {
         //由于j=Next[j],所以j不能为负;
        while(j!=-1 && y[i]!=x[j])//
        {
            j=Next[j];
        }
        i++;
        j++;//保证j不为负;
        if(j>=m)
        {
            ans++;
            j=Next[j];
        }
    }
    return ans;
}
int main()
{
    int n,nu,a1,b1;
    char a[10010],b[1000010];
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s%s",a,b);
        a1=strlen(a);
        b1=strlen(b);
        nu=KMP_count(a,a1,b,b1);
        printf("%d\n",nu);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值