您的位置:首页 > 其它

CodeVS1404 字符串匹配(扩展kmp)

2016-07-31 14:34 288 查看

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: