2022-11-12 Acwing每日一题

本系列所有题目均为Acwing课的内容,发表博客既是为了学习总结,加深自己的印象,同时也是为了以后回过头来看时,不会感叹虚度光阴罢了,因此如果出现错误,欢迎大家能够指出错误,我会认真改正的。同时也希望文章能够让你有所收获,与君共勉!

KMP字符串

给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。

模式串 P 在字符串 S 中多次作为子串出现。

求出模式串 P 在字符串 S 中所有出现的位置的起始下标。

输入格式
第一行输入整数 N,表示字符串 P 的长度。

第二行输入字符串 P。

第三行输入整数 M,表示字符串 S 的长度。

第四行输入字符串 S。

输出格式
共一行,输出所有出现位置的起始下标(下标从 0 开始计数),整数之间用空格隔开。

数据范围
1≤N≤105
1≤M≤106
输入样例:
3
aba
5
ababa
输出样例:
0 2

算法原理

如果需要得到一个字符串在另一个字符串中是否存在子串,以及子串的位置和个数的话,我们就可以使用KMP字符串匹配。
主要是通过由对于每一位最长的前后缀相等的子串的长度组成的next数组进行优化,当我们碰到两个字符串ps分别在j+1i位上的字符不相同,我们可以通过在模式串pnext数组来寻找j能回溯的最短距离,也就是他们匹配的最长距离。这样不仅保证对于s的每一位我们都可以进行匹配,同时j也不会直接回溯到初始点,只要他们有相同的前后缀。

代码实现

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000010;
char p[N],s[N];
int n,m;
int ne[N];

int main()  /// KMP:找到目标串中的子串的下标,返回所有的下标
{
    cin >> n >> p+1 >> m >> s + 1; // 从下标为1处开始存储字符串
    for(int i=2,j=0 ; i<=n ; ++i){	// next数组只跟模式串p有关,所以两个指针都在p上一个是0(后面使用会+1),一个是2
    	while(j && p[i] != p[j+1])	 j = ne[j];	// 如果不存在前后缀j=0,则退出,若存在前后缀且不匹配,则回溯
    	if(p[i] == p[j+1])	++j;	// 相等时才会++j,j=0时不会++j
    	ne[i] = j;	// 第i位上的字符回溯位置一定是j
	}
    
    
    for(int i=1,j=0; i<=n; ++i){
    	while(j && s[i] != p[j+1])	j = ne[j];	// 在第j位上回溯
    	if(s[i] == p[j+1])	++j;
    	if(j == n){
    		cout << i-n << ' ';
    		j = ne[j];	// 匹配成功可以回溯,也可以没有,在下一次循环时也会自动回溯的。
		}
	}
    
    return 0;
}
posted @   ZmQmZa  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示