KMP算法

题目描述

给出两个字符串 s1 和 s2 ,字符串满足长度小于100,000。现要求出s1中出现s2子串的所有位置。(位置编号从1到N)

输入

第一行输入N,M,N表示s1的长度,M表示s2的长度。

第二行输入字符串s1.

第三行输入字符串s2.

输出

   每一行表示子串s2在s1中出现的位置

样例输入 
14 7
abababaababacb
ababacb
样例输出 
8

OK,看到题目准备打暴力

但是仔细看看数据范围:给出两个字符串 s1 和 s2 ,字符串满足长度小于100,000。

还是算了吧🤮🤮

于是就有了这个万一的出现:KMP

是的,就是这个恶心的东西

好,那么暴力就不讲了,直接进入KMP的教学

KMP

首先,进行第一位的比较(i=j=1) ,发现对的上,那么继续

然后我们发现,当j=6时,对不上了

接着,做什么呢?

像暴力一样将i++吗? ——不不不

我们将j=next[j](什么是next[j]?),我相信你现在很急,但你先别急🙃🙃

什么是next[j]?

next[j]即:前缀后缀最长公共元素长度

什么,你问什么是前缀后缀最长公共元素长度?

举个例子,如果给定的模式串为“abab”,那么它的各个子串的前缀后缀的公共元素的最大长度如下表格所示:

也就是你当前这个字符串,从前面开始数与从后面开始数最多的相同字符个数

如何求?

cin >> n >> m >> s/*s1*/ >> t/*s2*/;
    int j = 0;
    for (int i = 1 /*i:枚举结束点*/; i < n; ++i) {
        // j:截取前j个字符
        while (j && t[i] != t[j] /*如果t[i]与t[j]匹配退出*/)
            j = nxt[j - 1]; // 更新至f[j-1](kmp公式)
        if (t[i] == t[j]) // 如果t[i]与t[j]匹配,看下一位
            j++;
        nxt[i] = j; // 记录
    }

好的接下来就是字符串匹配

字符串匹配

还是一样,可以匹配j++,不行的话j=nxt[j],i++

最后,匹配成功

好,上代码!

#include <bits/stdc++.h>
using namespace std;
int n, m;
int f[100020];
char s[100020], t[100020];
int main()
{
    cin >> n >> m >> s >> t;
    int j = 0;
    for (int i = 1 /*i:枚举结束点*/; i < n; ++i) {
        // j:截取前j个字符
        while (j && t[i] != t[j]/*如果t[i]与t[j]匹配退出*/)
            j = f[j - 1];//更新至f[j-1](kmp公式)
        if (t[i] == t[j])//如果t[i]与t[j]匹配,看下一位
            j++;
        f[i] = j;//记录
    }
    j = 0;
    for (int i = 0; i < n; ++i) {
        while (j && s[i] != t[j])
            j = f[j - 1];//同上
        if (s[i] == t[j])
            j++;//同上
        if (j == m) {
            cout << i - m + 2/*下表是0,+2*/ << endl;
            j = f[j - 1];//进行下一次寻找
        }
    }

    return 0;
}

如有疑问或疏忽请在评论区纠出,谢谢!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值