P3375 【模板】KMP

目录

题目描述

输入格式

输出格式

输入输出样例

说明/提示

样例 1 解释

数据规模与约定

代码

无注释版

有注释版


题目描述

给出两个字符串 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 长度
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值