目录
题目描述
给出两个字符串 s1 和 s2,若 s1 的区间 [l,r] 子串与 s2 完全相同,则称 s2 在 s1 中出现了,其出现位置为 l。
现在请你求出 s2 在 s1 中所有出现的位置。
定义一个字符串 s 的 border 为 s 的一个非 s 本身的子串 t,满足 t 既是 s 的前缀,又是 s 的后缀。
对于 s2,你还需要求出对于其每个前缀 s′ 的最长 border t′ 的长度。
输入格式
第一行为一个字符串,即为 s1。
第二行为一个字符串,即为 s2。
输出格式
首先输出若干行,每行一个整数,按从小到大的顺序输出 s2 在 s1 中出现的位置。
最后一行输出 ∣s2∣ 个整数,第 i 个整数表示 s2 的长度为 i 的前缀的最长 border 长度。
输入输出样例
输入
ABABABC
ABA
输出
1
3
0 0 1
说明/提示
样例 1 解释
。
对于 s2 长度为 3 的前缀 ABA
,字符串 A
既是其后缀也是其前缀,且是最长的,因此最长 border 长度为 1。
数据规模与约定
本题采用多测试点捆绑测试,共有 3 个子任务。
- Subtask 1(30 points):∣s1∣≤15,∣s2∣≤5。
- Subtask 2(40 points):∣s1∣≤104,∣s2∣≤102。
- Subtask 3(30 points):无特殊约定。
对于全部的测试点,保证 1≤∣s1∣,∣s2∣≤106,s1,s2 中均只含大写英文字母。
代码
无注释版
#include<bits/stdc++.h>
using namespace std;
char a[1000010],b[1000010];
int Next[1000010];
int main(){
cin>>a+1>>b+1;
int n=strlen(a+1);
int m=strlen(b+1);
for(int i=2;i<=m;i++){
int j=Next[i-1];
while(j&&b[j+1]!=b[i]) j=Next[j];
Next[i]=b[j+1]==b[i]?j+1:0;
}
int j=0;
for(int i=1;i<=n;i++){
while(j&&a[i]!=b[j+1]) j=Next[j];
if(a[i]==b[j+1]) j++;
if(j==m){
cout<<i-m+1<<"\n";
j=Next[j];
}
}
for(int i=1;i<=m;i++){
cout<<Next[i]<<" ";
}
}
有注释版
#include<bits/stdc++.h> // 引入 C++ 标准库,包含常用的输入输出、容器、算法等
using namespace std; // 使用标准命名空间,避免每次都加 std::
char a[1000010], b[1000010]; // 定义字符数组 a 和 b,分别存储字符串 s1 和 s2
int Next[1000010]; // 定义一个数组 Next,用于存储字符串 b 的前缀函数(KMP 算法的部分)
int main() {
cin >> a + 1 >> b + 1; // 输入两个字符串 s1 和 s2。字符串从下标 1 开始存储,避免从下标 0 开始
int n = strlen(a + 1); // 获取字符串 s1 的长度,注意数组下标从 1 开始
int m = strlen(b + 1); // 获取字符串 s2 的长度
// 计算字符串 b 的前缀函数 Next
for (int i = 2; i <= m; i++) { // 从 b 的第二个字符开始计算
int j = Next[i - 1]; // j 表示当前匹配的前缀的长度
// KMP 算法核心,寻找最长的可匹配前缀
while (j && b[j + 1] != b[i]) // 如果当前字符不匹配,回溯到前缀的前缀位置
j = Next[j]; // 回溯
// 如果匹配,则延长前缀长度,否则为 0
Next[i] = b[j + 1] == b[i] ? j + 1 : 0; // 如果 b[j+1] 和 b[i] 相同,更新 Next[i]
}
int j = 0; // 初始化 j 为 0,表示匹配 b 的起始位置
// 使用 KMP 算法在字符串 s1 中查找 s2 的所有出现位置
for (int i = 1; i <= n; i++) { // 遍历字符串 s1
// 如果当前字符不匹配,回溯到 Next 数组记录的位置
while (j && a[i] != b[j + 1])
j = Next[j]; // 回溯到匹配前缀的位置
if (a[i] == b[j + 1]) // 如果当前字符匹配,增加 j
j++;
// 如果 j == m,说明已经找到一个完整的匹配
if (j == m) {
cout << i - m + 1 << "\n"; // 输出匹配的位置(1-based index)
j = Next[j]; // 使用 Next 数组进行优化,继续匹配下一个可能的出现位置
}
}
// 输出字符串 s2 中每个前缀的最长 border 长度
for (int i = 1; i <= m; i++) {
cout << Next[i] << " "; // 输出 Next 数组的值,即每个前缀的最长 border 长度
}
}