acwing831 kmp算法 2021/11/24

 给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模板串 P 在模式串 S 中多次作为子串出现。

求出模板串 P 在模式串 S 中所有出现的位置的起始下标。

输入格式

第一行输入整数 N,表示字符串 P 的长度。

第二行输入字符串 P。

第三行输入整数 M,表示字符串 S 的长度。

第四行输入字符串 S。

输出格式

共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。

数据范围

1≤N≤105
1≤M≤106

输入样例:

3
aba
5
ababa

输出样例:

0 2

 首先我要推荐两个博主的解析,可以按顺序看,第一个是大概原理,然后第二个是比较详细的原理讲解,然后我又回去看了算法基础课的视频,觉得还是挺有帮助的

1.字符串匹配的KMP算法--前缀和后缀的详解_Wait for 的专栏-CSDN博客_kmp前缀后缀指的是什么

2.从头到尾彻底理解KMP(2014年8月22日版)_结构之法 算法之道-CSDN博客_kmp

对于第二个链接,我可以就是说两个点,ne数组的值就是存的前一个数的前缀后缀最大匹配值,然后就是ne数组匹配的时候,当匹配不上的时候,k = ne【k】(这个可以在第二个链接找详解)

然后下面的代码的ne【】数组是当前数的前缀后缀最大匹配值

我在纸上写了下运行过程

 这个运行样例有点特殊,因为它不存在匹配失败的情况。但是我不想它特殊,我就把详解的字母把空格去掉,就是7和21位字母作为模板串和模式串进行分析。

这个样例的过程花了我好多张纸,就不拍出来了,可以自己找画图软件啥的写一下,我不喜欢画图软件,我就在纸上写了。但是经过我很长时间的推理之后,发现模式串也存在特殊情况,因为最后不匹配的值正好是模板串的第一个数,所以我对模式串做了删改,改成了BBCABCDABCDABDE,这次是十五个字母,然后我在找规律。虽然挺麻烦的,但是毕竟我是刚学,能弄懂就好了。

 然后我把这个过程总结一下,上面的图片应该看不清(反正我就是用这个记录我的学习),但是这个样例我觉得是很好的

先说ne【】数组的取值,这里是每个下标的前缀后缀最大匹配值,当自身能匹配上的时候,则左指针值加一,相当于在i这个点,前缀后缀最大匹配值为j,然后当不匹配的时候,把坐标为j的前缀后缀最大匹配值赋值给j,一直到j=0或匹配成功,在这里是直接j变成0了,所以最后ne【7】=0,如果第7位数能匹配成功的话,那前缀后缀最大匹配数就会增加。

ne【】数组赋值完成后,就是模式串和模板串的匹配了,匹配是i匹配j+1,定义i=1,j=0,在这里我想说明一点,就是这个输出取决于j什么时候能成为模板串的长度,在j没有达到模板串的长度时,匹配成功一次,j+ 1 , 在匹配失败的时候,j就变成下标为j的前缀后缀最大匹配值,这里就相当于已经匹配成功了几位数,要找在已经匹配成功基础上,是否能继续匹配成功,然后继续这个过程,直到j=模板串的长度,然后输出i-length(模板串)(这个时候就是输出模式串的以0为下标,第一个与模板串相匹配的下标)

在输出之后,将模板串的前缀后缀最大匹配值赋值给j,看剩下的模式串还有没有机会再出现与模板串相同的组合。

这个是答案代码:-)

#include <iostream>

using namespace std;

const int N = 100100, M = 1000100;

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

int main()
{
    cin >> n >> p + 1 >> m >> s + 1;

    for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++ ;
        ne[i] = j;
    }

    for (int i = 1, j = 0; i <= m; i ++ )
    {
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        if (j == n)
        {
            printf("%d ", i - n);
            j = ne[j];
        }
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三粒小金子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值