Acwing KMP算法

KMP算法

主要应用:字符串匹配
比如我们从字符串"acfacfgded"(需要在哪里找的字符串称为“文本串”)找其中是否包含字符串"acfg"(需要从文本串里找的字符串我们叫做“模式串”),我们一般会想到的解法是暴力求解,两层for循环,依次对模式串的每一个元素进行匹配,如果匹配失败,下次还从模式串的第一个进行匹配,这就导致了较高的时间复杂度(O(n×m))。
而KMP算法不同之处就在于,当模式串的某个元素匹配失败后,不需要再从模式串的第一个元素从头开始匹配了,而是根据前缀表(next)找到模式串中一个最优的位置继续进行匹配

看具体题目Acwing 831.KMP字符串

给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 P在字符串 S中多次作为子串出现。
求出模式串 P在字符串 S中所有出现的位置的起始下标。

在这里插入图片描述
输入样例:

3
aba
5
ababa

输出样例:

0 2

思路分析:
1. next数组

核心:next[i]记录的是模式串下标i(包括i)之前的最长相等前后缀的长度。这里我们规定next[1]=0,因为第一个字符就不匹配,只能从0重新开始;

对于字符“acdac”

前缀后缀最长相等前后缀
a,ac,acd,acdacdac,dac,ac,cac

代码求解:思想类似KMP匹配两个字符串,特殊的只是将自己与自己进行匹配。

//求next的过程:本质为寻找字串中最长相等前后缀的长度
    for(int i = 2,j = 0;i <= n;i ++){//ne[1] = 0,第一个失败,只能从0开始
        while(j && p[i] != p[j+1]) j = ne[j];//退而求此次
        if(p[i] == p[j+1]) j ++;//看对眼,前进一步
        
        ne[i] = j;//p[1,j]==p[i-j+1,i]即这是最长相同前后缀的长度(j),
        //前缀为为1~j段;后缀为i-j+1~i这段
    }

2.KMP匹配过程

得到next数组之后,设指向文本串的指针为i,i从1开始,指向模式串的指针为j,j从0开始;文本串数组为s[],模式串数组为p[],next数组为ne[].文本串为“ababa”,模式串为“aba”

  • 开始循环比较s[i]与p[j+1],当出现j>0 且s[i]!=p[j+1]时,j指针跳到ne[j],即让模式串向右平移一段位置,而不是暴力那样直接模式串后移一位重新开始比较。

过程模拟如下:
在这里插入图片描述

kmp匹配过程板子:

//kmp匹配过程,利用next数组
    for(int i = 1,j = 0;i <= m;i ++){//i遍历s所有字母
        while(j && s[i] != p[j+1]) j = ne[j];//若上下不能匹配了,则j移动到ne[j]
        if(s[i] == p[j+1]) j++;//若已匹配,j后移一位
        
        if(j == n){
            //匹配成功
            printf("%d ",i-n);
            j = ne[j];//继续匹配下一个
        }
    }

具体实现代码:

#include <iostream>

using namespace std;

const int N=1e6+10;

int n,m;
char p[N],s[N];
int ne[N];

int main(){
    cin >> n >> p + 1 >> m >> s + 1;//下标从1开始
    //求next的过程:本质为寻找字串中最长相等前后缀的长度
    for(int i = 2,j = 0;i <= n;i ++){//ne[1] = 0,第一个失败,只能从0开始
        while(j && p[i] != p[j+1]) j = ne[j];//退而求此次
        if(p[i] == p[j+1]) j ++;//看对眼,前进一步
        
        ne[i] = j;//p[1,j]==p[i-j+1,i]即这是最长相同前后缀的长度(j),
        //前缀为为1~j段;后缀为i-j+1~i这段
    }
    //kmp匹配过程,利用next数组
    for(int i = 1,j = 0;i <= m;i ++){//i遍历s所有字母
        while(j && s[i] != p[j+1]) j = ne[j];//若上下不能匹配了,则j移动到ne[j]
        if(s[i] == p[j+1]) j++;//若已匹配,j后移一位
        
        if(j == n){
            //匹配成功
            printf("%d ",i-n);
            j = ne[j];//继续匹配下一个
        }
    }
    
    return 0;
}

以上就是KMP算法的基本方法,关键在于next数组的计算,其代码实现相比原理来说更为简单,建议可以多些几遍,加深记忆。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值