题目描述
给定一个数字串,请你输入一个字符串,是否可以与模板串进行匹配,如果匹配成功,则输出相同字符串在给定字符串的头位置信息,否则输出“Not Find!”。
样例输入
第一行输入测试样例数,第二行输入两个数字串的大小,第三、四行输入两个数字串。
1
8 5
1 2 3 4 5 6 7 8
3 4 5 6 7
样例输出
Find at:3
分析
字符串匹配问题,用KMP算法进行求解。
第一步:求解出模板串b[ ]的前缀表,也就是人们常说的Next[ ]数组,此篇文章用pre_table[ ]表示该前缀表;
第二步:kmp算法在模板串b[ ]和文本串a[ ]间进行字符串匹配;
【例子】
已知一个模板数字串b[8]={4,5,4,2,4,5,4,4},则该字符串的前缀表pre_table[ ]={0,0,1,0,1,2,3,1};
求解过程如图所示:
求出了pre_table[ ]之后,便于后边进行字符串匹配工作,将pre_table[ ]数组向后移动一位,并将pre_table[0]置为-1,此例pre_table[ ]={-1,0,0,1,0,1,2,3};
然后进行字符串匹配,a[i]、b[j]由i=0,j=0开始进行逐一匹配,当a[i]!=b[j]时,将j=pre_table[j],再次进行字符匹配。
代码示例
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int MAXN = 100;
void pre_table(int a[],int n,int pre[]);
void move_pre_table(int n,int pre[]);
void kmp(int a[],int b[],int m,int n,int pre[]);
int main()
{
int num;
scanf("%d",&num);
while(num--)
{
int m,n;
int a[MAXN],b[MAXN];
int pre[MAXN];
scanf("%d%d",&m,&n);
for(int i=0; i<m; i++)
{
scanf("%d",&a[i]);
}
for(int i=0; i<n; i++)
{
scanf("%d",&b[i]);
}
pre_table(b,n,pre);
//测试模式数字串b[]的前缀数组的输出
/*
for(int i=0; i<n; i++)
cout<<pre[i];
*/
move_pre_table(n,pre);
kmp(a,b,m,n,pre);
}
return 0;
}
void pre_table(int a[],int n,int pre[])
{
pre[0]=0;
int i=1,len=0; //len表示a[]相同前、后缀的最大长度,初始化为0
while(i<n)
{
if(a[len]==a[i])
{
len++;
pre[i]=len;
i++;
}
else
{
if(len>0)
len=pre[len-1];
else
{
pre[i]=len;
i++;
}
}
}
}
void move_pre_table(int n,int pre[])
{
for(int i=n;i>0;i--)
{
pre[i]=pre[i-1];
}
pre[0]=-1;
}
void kmp(int a[],int b[],int m,int n,int pre[])
{
int i=0,j=0;
while(i<m)
{
if(j==n-1)
{
if(a[i]==b[j])
{
printf("Find at:%d",i-j+1);
j=pre[j];
}
else
printf("Not find!");
}
if(a[i]==b[j])
{
i++;
j++;
}
else
{
j=pre[j];
if(j==-1)
{
i++;
j++;
}
}
}
}