ACM #1015 KMP算法

#1015 : KMP算法

时间限制:1000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。

这一天,他们遇到了一只河蟹,于是河蟹就向小Hi和小Ho提出了那个经典的问题:“小Hi和小Ho,你们能不能够判断一段文字(原串)里面是不是存在那么一些……特殊……的文字(模式串)?

小Hi和小Ho仔细思考了一下,觉得只能想到很简单的做法,但是又觉得既然河蟹先生这么说了,就肯定不会这么容易的让他们回答了,于是他们只能说道:“抱歉,河蟹先生,我们只能想到时间复杂度为(文本长度 * 特殊文字总长度)的方法,即对于每个模式串分开判断,然后依次枚举起始位置并检查是否能够匹配,但是这不是您想要的方法是吧?”

河蟹点了点头,说道:”看来你们的水平还有待提高,这样吧,如果我说只有一个特殊文字,你能不能做到呢?“

小Ho这时候还有点晕晕乎乎的,但是小Hi很快开口道:”我知道!这就是一个很经典的模式匹配问题!可以使用KMP算法进行求解!“

河蟹满意的点了点头,对小Hi说道:”既然你知道就好办了,你去把小Ho教会,下周我有重要的任务交给你们!“

”保证完成任务!”小Hi点头道。

提示一:KMP的思路

提示二:NEXT数组的使用

提示三:如何求解NEXT数组

输入

第一行一个整数N,表示测试数据组数。

接下来的N*2行,每两行表示一个测试数据。在每一个测试数据中,第一行为模式串,由不超过10^4个大写字母组成,第二行为原串,由不超过10^6个大写字母组成。

其中N<=20

输出

对于每一个测试数据,按照它们在输入中出现的顺序输出一行Ans,表示模式串在原串中出现的次数。



题目很明确,就是编写KMP算法。那就先来解释一下KPM算法的思路。

有两个字符串,目标字符串W和模式字符串T。当W和T失配后,并不是简单地从目标串下一个字符开始新一轮的检测,而是依据模式字符串的next数组,从字符串T的某个位置直接继续匹配。这样就提高了效率。

next数组的作用:指导下一次该匹配的位置。

比如字符串T为bbsbbc,当在字符c处失配则下一次匹配可以从s字符串开始,因为c前面的字符bb和s前面的字符bb是相同的,所以可以直接到s。当在s处失配的话,下一次可以从第二个b开始,因为b前面肯定是b,前面已经匹配过了。如此我们可以发现规律,在哪里发生失配,只要比较它前面的字符串中前缀和后缀的相同个数,从而求出next数组。那么如何计算next数组?

设next[0]为-1,接着看j=1,T[1]=b前面只有一个,所以肯定是0。j=2,T[2]=s。前缀是b,后缀也是b,有一个相等,所以next[2]=1。j=3,T[3]=b。前面是bbs,前缀和后缀都不相等,所以next[3]=0。以此类推,可以总结为前缀就是从字符串初始位置开看,后缀到当前字符串为止去看。前缀后缀有多少个字符串相等,next[j]就等于多少。下面来看代码:

//计算next数组,作用是指导下一次该匹配的位置
void getNext(string T, int *next)
{
	next[0] = -1;//第一位就不匹配,则返回-1

	int i = 0, j = -1;
	
	int str_size = T.size();
	while (i < str_size)
	{
		//i是后缀,j是前缀
		if (j == -1 || T[i] == T[j])
		{
			i++;
			j++;
			next[i] = j;
		} else {
			j = next[j];//j回溯,后缀是相对的。
		}
	}


}
选一个字符串对着代码推一遍应该就能懂的。

搞懂了next数组,匹配就很简单了。不多说,直接看代码吧

//匹配算法。
//思路:当目标字符串与模式字符串不匹配时,根据next数组将模式字符串回朔到某个位置,而不是从头开始匹配
int matchString(string T, string W, int *next)
{
	int i = 0, j = 0;
	int sum = 0;
	int T_size = T.size();
	int W_size = W.size();
	
	while(i < W_size)
	{
		if (W[i] == T[j])
		{
			//模式字符串全部相同时,说明目标串中有相同字符串
			if (j == (T_size - 1))
			{
				sum++;
				j = 0;
			}
			i++;
			j++;
		} else {
			//在不匹配位置,根据next数组将模式字符串回溯到已经匹配过的地方
			j = next[j];
		}

		//如果第一个就不匹配,则都+1
		if (j == -1)
		{
			i++;
			j++;
		}
	}

	return sum;
}


int main()
{
	int n = 0;//表示测试数据组数。
	cin >> n;
	string T;//模式字符串
	string W;//目标字符串
	int next[255];
	int out[255];

	for(int i = 0; i < n; i++)
	{
		cin >> T;
		cin >> W;
		getNext(T, next);
 		int sum = matchString(T, W, next);
 		out[i] = sum;
	}

	for (int i = 0; i < n; i++)
	{
		cout << out[i] <<endl;
	}

	return 0;
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值