【算法——KMP】:acwing模板理解以及DS串应用--KMP算法

一、acwing

整体思路:

  我们不想要像暴力那样,一个位置不匹配就往下一位继续匹配 

 我们可以找到某个位置让模板串的前部分都相同,我们只用去匹配下一个位置是否相同即可

观察可发现,ABCD四个位置都是相同的

   找到相同部分AC就等于找到相同部分BD所以,我们的问题就转化为找到模板串他的前后缀公共数量的值。所以我们需要做一个预处理,求ne数组,得到每个位置的前后缀公共数量值了,然后在匹配的时候,通过ne数组我们可以快速得到A的长度j,从而方便我们去匹配i和j+1的位置。

  为什么要找这个呢,因为我们想要节省一点时间,所以我们想要找到模板串从开头开始某个j长度的部分和我失配位置前的j个长度是相同的,那么我们就不用再去比较前前面的了,只用继续往下匹配就好。

     如果想要匹配,模板串开头开始j的长度的部分一定i前部分一定要匹配得上我们才会开始匹配第i个位置,不然根本不会轮到i位置比较,因为前面都失配了。

   1.模板

题目:

模板:

#include <iostream>

using namespace std;

const int N = 1000010, M = 1000010;

int n, m, ne[N];
char s[N], p[N];

int main(){
    cin >> n >> p + 1 >> m >> s + 1;
    for (int i = 2, j = 0; i <= n ; i ++ ){
        while(j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++;
        ne[i] = j;
    }
    
    for (int i = 1, j = 0; i <= m ; i ++ ){
        while(j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++;
        if (j == n){
           printf("%d ",i-n);
            j =ne[j];
        }
    }
    
    return 0;
}

2.模板理解

#include <iostream>

using namespace std;

const int N = 1000010, M = 1000010;

int n, m, ne[N];
char s[N], p[N];
//这里s为长串,也就是被匹配的那一个
//p为短串,也就是匹配的那一个
int main(){
    cin >> n >> p + 1 >> m >> s + 1;
//这里是求next数组
//next数组的下标对应字符串的位置(这里的字符串我们是从1开始算位置的)
//而数组里面的值对应的是每个位置最大的前后缀公共数量的值
//比如abcab,ne[2]对应的是b,b前缀是a,后缀是b,没有公共部分,所以ne[2]=0,
//而ne[4]对应的是a,他有一个前缀为a,但是再往后ab和ca不对应,所以ne[4]只能是1,不能是2

//这里为什么要从2开始呢
//因为第一个字符他一定没有前缀,所以根本不用匹配,所以我们从第二个开始
//这里的j是我们前、后缀的长度
    for (int i = 2, j = 0; i <= n ; i ++ ){
        while(j && p[i] != p[j + 1]) j = ne[j];
//我们匹配的位置是i和j+1,所以这里的ne[j]当中的j的意义是说,我们j+1不匹配,
//我们的j就要回到上一个位置的ne的值,所以等号左边的j是长度的意思,右边的j是指位置。
        if (p[i] == p[j + 1]) j ++;
//如果匹配的话,我们就继续增加前后缀长度,看看下一个位置是否能继续相等
        ne[i] = j;
    }
    


//这里开始真正匹配

    for (int i = 1, j = 0; i <= m ; i ++ ){
        while(j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++;
        if (j == n){//j的长度为匹配的p的长度,说明我们已经找到了一个和模板串匹配的位置
           printf("%d ",i-n);//i为我们匹配结束的位置,不是开始匹配的位置,所以一定要记得减去长度才是我们开始匹配到字符串的位置
            j = ne[j];
        }
    }
    
    return 0;
}

二、DS串应用--KMP算法

题目描述

学习KMP算法,给出主串和模式串,求模式串在主串的位置

算法框架如下,仅供参考

输入

第一个输入t,表示有t个实例

第二行输入第1个实例的主串,第三行输入第1个实例的模式串

以此类推

输出

第一行输出第1个实例的模式串的next值

第二行输出第1个实例的匹配位置,位置从1开始计算,如果匹配成功输出位置,匹配失败输出0

以此类推

代码:

#include<iostream>
#include<cstring>
using namespace std;

string s;
string p;
int ne[1010];
int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		cin >> s >> p;
		int slen = s.length();
		int plen = p.length();
		s = "0" + s;;
		p = "0" + p;
		//j是最大相同前后缀和
		//为什么从2开始呢,因为第一个字符没有前缀所以我们可以直接跳过
		for (int i = 2,j=0; i <= plen; i++)
		{
			while (j && p[i] != p[j+1]) 
			//就是我们希望找到某一段的j和当前部分i是匹配的,所以我们需要计算前后缀和, 
			//
			{
				j = ne[j];
				//这里其实就是!!!!j+1的前一个!,所以是j!!!
			}
			if (p[i] == p[j+1]) 
				j++;
			ne[i] = j;
		}
		cout << "-1 ";
		for (int i =1;i<plen; i++)
		{
			cout << ne[i] << " ";
		}
		cout << endl;
		int flag = 0;
		for (int i =2, j = 0; i <= slen; i++)
		{
			while (j && s[i] != p[j + 1]) j = ne[j];
			if (s[i] == p[j + 1]) j++;
			//就是刚好匹配到一个完整的子串
			if (j ==plen)
			{
				cout << i-j+1<<endl;
				flag = 1;
				break;
			}
		}
		if (flag == 0) cout << "0" << endl;
		memset(ne, 0, sizeof ne);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值