KMP算法

题目

【模板】KMP - 洛谷

给出两个字符串s1和s2​,若s1某个区间的子串与s2完全相同,则称s2在s1​中出现了,现在请你求出s2​在s1中所有出现的位置。

解题

KMP模板题

首先思考最朴素的做法,就是两层for循环,时间复杂度O(nm)。

但是其实,每次遇到不匹配的字母,我们并不需要在s1上逐个移动,每次移动对s2的所有字符逐个判定,而是可以通过使s1和s2上某个特定的字符(串)对齐,来实现移动。

而这个“特定的字符(串)”是可以通过预处理存在一个数组next中的,这样一来,每次移动的操作就由O(n)降为O(1)。

要使s1和s2匹配,我们的想法是,首先对s1进行遍历,在遍历过程中,判断s1和s2对应位置是否相同,如果不相同,就进行移动,让s1的当前位置与s2的next[j](j是当前s2中失配的位置)位置对齐;如果此时对应位置上的元素相同,则j++,表示又成功匹配了一位。当j==s2长度时,匹配成功。

要怎么实现next数组的预处理呢?我们采用的思想是让s2串自己匹配自己。

也就是说,我们对s2进行遍历,在遍历过程中,如果当前字符失配了,就直接跳到next数组所指的下一个位置。如果成功匹配,则j++,即j向后移一位;再使next[i]=j(j表示匹配到s2的第j位了),即当下次s2中某一位失配时(比如说第i+1位),我们可以直接将第i位跳到s2中的第j位与其进行匹配。换句话说,s2中第i位和第j位的元素是相同的,在s1和s2的匹配的移动过程中,我们可以通过将s1的某一位分别与s2的第i位、第j位对齐来完成一次移动。

#include<iostream>
#include<cstring>
using namespace std;
const int N=1e6+10;
char ts1[N],ts2[N];
int ne[N];
int main()
{
    cin>>ts1;
    cin>>ts2;
    int n=strlen(ts1);
    int m=strlen(ts2);
    char s1[N],s2[N];
    for(int i=1;i<=n;i++) s1[i]=ts1[i-1];
    for(int i=1;i<=m;i++) s2[i]=ts2[i-1];
    
    for(int i=2,j=0;i<=m;i++)
    {
        while(j&&s2[i]!=s2[j+1]) j=ne[j];
        if(s2[i]==s2[j+1]) j++;
        ne[i]=j;
    }
    for(int i=1,j=0;i<=n;i++)
    {
        while(j&&s1[i]!=s2[j+1]) j=ne[j];
        if(s1[i]==s2[j+1]) j++;
        if(j==m)
        {
            printf("%d\n",i-m+1);
            j=ne[j];
        }
    }
    for(int i=1;i<=m;i++) cout<<ne[i]<<" ";
}

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值