KMP--找子串2.0

问题概述:输入两个数n和m,再依次输入n个数作为串a,再输入m个数作为串b,请判断b串是否为a的子串,若是则输出b串在a串的起始点

输入样例:                                                                        对应输出:

2                                                                              6

13 5

1 2 1 2 3 1 2 3 1 3 2 1 2

1 2 3 1 3


KMP算法(专用于求解字符串匹配问题)步骤:

①定义一个数组next[m]用于对应子串(b[m]),next[i]=j表示b[1~j]和b[i-j+1, i]完全相等且b[i]!=b[j]

②初始化i=j=1(串中第一个)

③i++, j++,如果a[i]==b[i]执行步骤5,否则执行步骤4

④b串向后挪,由b[j]对应a[i]变为b[next[j]]对应a[i](j=next[j]),如果next[j]==-1则表示a[i]完全不能对应字串b中的任何一个元素,i++, j=0

⑤判定是否已经匹配完毕,如果没有继续执行步骤3

(在最后3个步骤中,如果i超过a的范围,表示匹配失败,结束)

原理:http://www.matrix67.com/blog/archives/115

复杂度:O(n+m)


#include<stdio.h>
#include<string.h>
int n, m;
int a[1000005], b[10005], next[10005];
void Getnext();
int KMP();
int main(void)
{
	int T, i;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d%d", &n, &m);
		for(i=1;i<=n;i++)
			scanf("%d", &a[i]);
		for(i=1;i<=m;i++)
			scanf("%d", &b[i]);
		if(n<m)
		{
			printf("-1\n");
			continue;
		}
		Getnext();
		printf("%d\n", KMP());
	}
	return 0;
}
void Getnext()
{
	int i, j;
	i = 1, j = 0;
	next[1] = 0;
	while(i<=m)
	{
		if(j==0 || b[i]==b[j])
		{
			i++, j++;
			if(b[i]==b[j])	/*虽然到这里可以满足b[1~j]和b[i-j+1,i]完全相等,但是因为b[i]=b[j],如果b[i]无法匹配,那么b[j]一定无法匹配*/
				next[i] = next[j];
			else
				next[i] = j;
		}
		else    /*如果b[i]和b[j]不相等,则说明后面不可能存在在i-j=k(k为定值)的情况下b[1~j]和b[i-j+1,i]完全相等,拉开i与j的距离*/
			j = next[j];
	}
}
int KMP()
{
	int i, j;
	i = j = 1;
	while(i<=n)
	{
		/*		统计数量
		if(a[i]==b[j])
		{
			if(j==m)
			{
				ans++;
				j = net[j+1];
				i++;
			}
			else
				i++, j++;
		}
		*/
		if(a[i]==b[j])		/*匹配成功,i++,j++,继续匹配*/
		{
			if(j==m)
				return i-j+1;
			i++, j++;
		}
		else
		{
			j = next[j];		/*匹配不成功,b串向后挪,由b[j]对应a[i]变为b[next[j]]对应a[i],即j=next[j]*/
			if(j==0)	/*如果next[j]==-1表示a[i]完全不能对应字串b中的任何一个元素,i++,j=0*/
				i++, j = 1;
		}
	}
	return -1;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值