CodeVS1404 字符串匹配(扩展kmp)

CodeVS1404 字符串匹配 扩展kmp讲解

题目描述 Description

给你两个串A,B,可以得到从A的任意位开始的子串和B匹配的长度。
给定K个询问,对于每个询问给定一个x,求出匹配长度恰为x的位置有多少个。
N,M,K<=200000

输入描述 Input Description

第一行三个数 N,M,K,表示A的长度、B的长度和询问数。
第二行为串A。
第三行为串B。
接下来K行,每行1个数X。

输出描述 Output Description

对于每个询问输出一个数。

样例输入 Sample Input

6 2 2
aabcde
ab
0
2

样例输出 Sample Output

4
1

数据范围及提示 Data Size & Hint

各个测试点1s

分析

        假如有匹配串A长度为N,模式串B长度为M,那么扩展KMP算法可以在O(N+M)的时间内算出对于A的每一个位置,与B的最长匹配长度是多少(即与B串前缀重合的最长长度),算法如下:

        PART_1 初始化next数组

        设next[i]表示B串的i位置开始的字符串与B串的前缀的最长重合长度(注意这里与KMP中的next是不一样的)。明显地,next[1]=M(我的字符串的下标习惯从1开始),next[2]也可以用一个简单的for循环求出,pos初始化为2;当i∈[3,M]时,我首先认为关于1~i-1的信息已全部求出,我们记录一个pos,它的意义是当前已计算出的next数组中,j+next[j]-1能达到的最右端所对应的i,这个不太好理解,可以参考manacher算法的思想戳这(其实manacher和扩展kmp是很像的),令rp=pos+next[pos]-1

        (1)当i+next[i-pos+1]<rp时,易得next[i]=next[i-pos+1]

        (2)当i+next[i-pos+1]>=rp时,rp后的元素,即b[rp+j],可能会和b[rp-i+1+j]相等,这时进行暴力扩展,如果扩展发生,则更新pos为i,next[i]直接在扩展中求出。

        注意:有时rp可能小于i,这时可以加一特判,直接进行暴力求

        PART_2 匹配

        令ans[i]表示a串中以a[i]为开头的后缀和b串的最长匹配长度,设pos表示a串中对于所有的i,i+ans[i]-1能达到的最远位置对应的i,rp就是pos+ans[pos]-1,next[1]暴力求出,pos初始化为1;当i∈[2,N]时:

        (1)若i+next[i-pos+1]-1<rp,则ans[i]=next[i-pos+1]

        (2)若i+next[i-pos+1]-1>=rp,则进行暴力扩展,同时更新pos,ans在扩展时求出

        注意:如果rp<i,那么加一特判,直接暴力扫描

代码

//CodeVS1404 字符串匹配 扩展KMP
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxlen 200010
using namespace std;
char a[maxlen], b[maxlen];
int next[maxlen], N, M, K, cnt[maxlen], ans[maxlen], tong[maxlen];
void init()
{
	int i, j, p, a;
	next[1]=M;
	for(i=1;b[i]==b[i+1];i++);
	next[2]=i-1;
	a=2;
	for(i=3;i<=M;i++)
	{
		p=a+next[a]-1;
		if(i+next[i-a+1]-1>=p)
		{
			if(p<i)
			{
				for(j=i;b[j]==b[j-i+1];j++);j--;
				if(b[j]==b[j-i+1]&&j>=i)next[i]=j-i+1;
			}
			else
			{
				for(j=1;b[p+j]==b[p-i+1+j];j++);
				next[i]=p-i+j;
			}
			if(i+next[i]-1>p)a=i;
		}
		else next[i]=next[i-a+1];
	}
}
void exkmp()
{
	int i, j, rp, pos;
	for(i=1;i<=min(N,M)&&a[i]==b[i];i++);i--;
	ans[1]=i;
	tong[i]++;
	pos=1;
	for(i=2;i<=N;i++)
	{
		rp=pos+ans[pos]-1;
		if(i+next[i-pos+1]-1>=rp)
		{
			if(rp<i)
			{
				for(j=i;a[j]==b[j-i+1];j++);j--;
				if(a[j]==b[j-i+1]&&j>=i)ans[i]=j-i+1;
			}
			else
			{
				for(j=rp+1;a[j]==b[j-i+1];j++);j--;
				ans[i]=j-i+1;
			}
		}
		else ans[i]=next[i-pos+1];
		if(i+ans[i]-1>rp)pos=i;
		tong[ans[i]]++;
	}
}
int main()
{
	int q;
	scanf("%d%d%d",&N,&M,&K);
	scanf("%s%s",a+1,b+1);
	init();
	exkmp();
	while(K--)
	{
		scanf("%d",&q);
		printf("%d\n",tong[q]);
	}
	return 0;
}

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/fsahfgsadhsakndas/article/details/52078484
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞