【Week15作业 C】ZJM与纸条【KMP】

题意:

ZJM 的女朋友是一个书法家,喜欢写一些好看的英文书法。有一天 ZJM 拿到了她写的纸条,纸条上的字暗示了 ZJM 的女朋友 想给 ZJM 送生日礼物。ZJM 想知道自己收到的礼物是不是就是她送的,于是想看看自己收到的礼物在纸条中出现了多少次。

第一行输入一个整数代表数据的组数
每组数据第一行一个字符串 P 代表 ZJM 想要的礼物, 包含英语字符 {‘A’, ‘B’, ‘C’, …, ‘Z’}, 并且字符串长度满足 1 ≤ |P| ≤ 10,000 (|P| 代表字符串 P 的长度).
接下来一行一个字符串 S 代表 ZJM 女朋友的纸条, 也包含英语字符 {‘A’, ‘B’, ‘C’, …, ‘Z’}, 满足 |P| ≤ |S| ≤ 1,000,000.

输出一行一个整数代表 P 在 S中出现的次数.


思路:

使用KMP算法来求解,KMP是在暴力匹配算法的基础上进行优化的算法,它跳过了那些绝不可能成功的字符串比较。
令Next[i]的值是使得P[0…i]这一个子串的K-真前缀等于K-真后缀的最大的K。那么如果在P[r]失配,那么对于P[0…r-1]这一段,前Next[r-1]个字符一定和后Next[r-1]个字符相等,可以拿长度为Next[r-1]的前缀来替代当前的后缀,让P[Next[r-1]]这个字符对准刚刚失配的位置,从而跳过不可能成功的字符串比较。
接下来要求Next数组。显然Next[0]=0,要递推计算Next数组,即已知Next[0…x-1],求Next[x]。简记Next[x-1]为now,共有两种情况:情况1,P[x]=P[now],说明P[0…x]相等的前后缀是字符串P[0…x-1]加上P[x],即Next[x]=now+1;情况2,P[x]!=P[now],尝试缩短now,但仍要保证对于P[0…x-1]而言,它的now-真前缀等于now-真后缀,即now=Next[now-1]。


总结:

一道KMP算法求解字符串问题,需要好好理解一下KMP算法。这道题用G++可能会T,但相同代码用C++就能A,很神奇。


代码:

#include <iostream>
#include <string>
using namespace std;

int zs;
int Next[10010];
void getNext(string ptr)
{
	Next[0]=0;
	for(int i=1,j=0;i<ptr.size();i++)
	{
		while(j&&ptr[i]!=ptr[j])
			j=Next[j-1];
		if(ptr[i]==ptr[j])
			j++;
		Next[i]=j;
	}
}
int KMP(string str,string ptr)
{
	int cnt=0;
	getNext(ptr);
	for(int i=0,j=0;i<str.size();i++)
	{
		while(j&&str[i]!=ptr[j])
			j=Next[j-1];
		if(str[i]==ptr[j])
			j++;
		if(j==ptr.size())
		{
			cnt++;
			j=Next[j-1];
		}
	}
	return cnt;
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>zs;
	string s1,s2;
	while(zs--)
	{
		cin>>s1>>s2;
		int ans=KMP(s2,s1);
		cout<<ans<<endl;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值