链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
给出两个只含大写英文字母的字符串 s1s_1s1 和 s2s_2s2,若 s1s1s1 的区间[l,r][l,r][l,r]子串与 s2s_2s2 完全相同,则称s2s_2s2在 s1s_1s1 中出现了,其出现位置为lll。
现在请你求出 s2s_2s2在 s1s_1s1 中所有出现的位置。
定义一个字符串 sss 的 border 为 sss 的一个非sss本身的子串ttt,满足 ttt 既是 sss 的前缀,又是 sss 的后缀。
对于 s2s_2s2,你还需要求出对于其每个前缀 s′s's′ 的最长 border t′t't′ 的长度。
输入描述:
第一行为一个字符串,即为 s1s_1s1
第二行为一个字符串,即为 s2s_2s2
1≤∣s1∣,∣s2∣≤1061\le|s_1|,|s_2| \le 10^61≤∣s1∣,∣s2∣≤106
输出描述:
首先输出若干行,每行一个整数,按从小到大的顺序 输出 s2s_2s2 在 s1s_1s1 中出现的位置 最后一行输出 ∣s2∣|s_2|∣s2∣ 个整数,第 iii 个整数表示 s2s_2s2 的长度为 iii 的前缀的最长 border 长度。
输入:
ABABABC
ABA
输出:
1
3
0 0 1
答案:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 1e6 + 5;
int n, m, ne[N];
string s, t;
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> s >> t;
n = s.length();
m = t.length();
for(int i = 2; i <= m; ++ i) {
int j = i - 1;
while(j) {
if(t[ne[j]] == t[i - 1]) {
ne[i] = ne[j] + 1;
break;
}
j = ne[j];
}
}
for(int i = 1, j = 0; i <= n; ++ i) {
while(j && s[i - 1] != t[j]) {
j = ne[j];
}
j += (s[i - 1] == t[j]);
if(j == m) {
cout << i - m + 1 << '\n';
j = ne[j];
}
}
for(int i = 1; i <= m; ++ i) {
cout << ne[i] << ' ';
}
return 0;
}
解释:
下面的部分即为计算第二个字符串的prefix_table(叫法不一样但原理都是该字符串对应的用于比较前后缀所得到的数组)在此处命名为ne数组
for(int i = 2; i <= m; ++ i) {
int j = i - 1;
while(j) {
if(t[ne[j]] == t[i - 1]) {
ne[i] = ne[j] + 1;
break;
}
j = ne[j];
}
}
下列代码为两个字符串比较的过程运用KMP的方法比较
for(int i = 1, j = 0; i <= n; ++ i) {
while(j && s[i - 1] != t[j]) {
j = ne[j];
}
j += (s[i - 1] == t[j]);//(s[i - 1] == t[j])相等时输出1;
if(j == m) {
cout << i - m + 1 << '\n';
j = ne[j];
}
}
ai解释:
这段代码实现的是KMP(Knuth-Morris-Pratt)算法,用于在一个文本字符串s
中查找一个模式字符串t
的所有出现位置,并输出这些位置的起始索引(从1开始计数),以及在最后输出模式字符串t
的部分匹配表(也称为失败函数或前缀函数)。
KMP算法的核心思想:
-
预处理部分匹配表(Next数组):首先,对模式字符串
t
进行预处理,构建一个部分匹配表(或称为前缀函数表),这个表用于在模式字符串自身出现不匹配时,决定模式字符串应该从哪里开始重新与文本字符串进行比较,以避免从头开始比较的浪费。 -
搜索文本字符串:使用预处理得到的部分匹配表,在文本字符串
s
中搜索模式字符串t
。如果发生不匹配,则根据部分匹配表决定模式字符串应该跳转到哪个位置继续比较。
代码解析:
- 部分匹配表(Next数组)的构建:
- 数组
ne
用于存储部分匹配表,其中ne[i]
表示模式字符串t
中,以t[i-1]
结尾的最长相同前后缀的长度(不包括t[i-1]
自身)。 - 使用
j
变量表示当前正在比较的前缀的末尾位置,通过迭代计算ne
数组的值。
- 数组
- 在文本字符串中搜索模式字符串:
- 使用两个指针
i
和j
分别遍历文本字符串s
和模式字符串t
。 - 当
s[i-1]
与t[j]
不匹配时,根据ne[j]
的值调整j
的位置,继续比较。 - 如果
j
等于模式字符串的长度m
,则说明找到了一个匹配,输出匹配开始的位置(i-m+1
),并调整j
为ne[j]
以继续搜索下一个可能的匹配。
- 使用两个指针
- 输出部分匹配表:
- 在所有匹配搜索完成后,输出模式字符串
t
的部分匹配表。
- 在所有匹配搜索完成后,输出模式字符串
注意事项:
- 代码中使用了
cin.tie(0)->sync_with_stdio(0);
来加速输入输出,这是C++中常用的优化技巧,但在某些环境下(如某些在线编程平台)可能不起作用或导致不可预测的行为。 - 数组
ne
的初始化(即ne[0]
的值)在代码中未显式设置,但在KMP算法中,通常将ne[0]
设置为-1或0(取决于具体实现),这里由于C++的全局变量自动初始化为0,所以ne[0]
默认为0,这在KMP算法中是合理的。 - 代码中直接使用了
string
类型来存储文本和模式字符串,简化了字符串处理的操作。 - 输出匹配位置时,是从1开始计数的,这符合很多编程竞赛和实际应用场景的需求。