题目描述
给出两个字符串 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;
}
如有疑问或疏忽请在评论区纠出,谢谢!