Messenger CodeForces - 631D(KMP)

Messenger

题目连接:CodeForces - 631D

题意:给出两个字符串t, s,问s在t中出现了几次;但是字符串给出的形式是分块:如下:len-c;len表示长度,c表示一个字符,是说共有len个c字符;例如:3-a 4-c 2-f 表示"aaaccccff";

思路:一开始想把字符串展开,但是共有200000块,每块长度为1000000;所以最长的字符串是200000000000长度,根本存不下来,就算存下来,遍历一遍就超时了;所以就想直接匹配块;首先把相邻的,同种字符合并;然后再用KMP算法匹配;有一点要注意,匹配的时候对于第一块和最后一块,并不需要对应严格相等,只要对应块的字符相同,并且原始串中对应块的长度大于匹配串就可以,但是中间的块必须严格相等;所以我们可以只匹配中间块,中间串匹配成功后在匹配首尾块;所以当只有1或2块时需要特殊判断;要用long long;

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
struct node{
	ll len;
	char ch;
}p;
int nxt[maxn];
bool ok(node a, node b){
	if(a.len==b.len&&a.ch==b.ch) return true;
	return false;
}
void cal_next(node *r, int len){
	nxt[0]=-1;
	int j=0, k=-1;
	while(j<len){
		if(k==-1||ok(r[k], r[j])) j++, k++, nxt[j]=k;
		else k=nxt[k];
	}
}
bool judge(node a, node b){
	if(a.ch==b.ch&&a.len>=b.len) return true;
	return false;
}
ll KMP(node *s, int n, node *t, int m, node a, node b){
	cal_next(t, m);
	int i, j=0;
	ll cnt=0;
	for(i=1; i<n-1; i++){//从第二块开始匹配;
		while(j&&!ok(s[i], t[j])) j=nxt[j];
		if(ok(s[i], t[j])) j++;
		if(j==m){
			if(judge(s[i+1], b)&&judge(s[i-m], a)) cnt++;//中间块匹配成功后,判断首尾;
			j=nxt[j];
		}
	}
	return cnt;
}
ll ans1(node *s, int len, node a){//特判只有一块;
	ll cnt=0;
	for(int i=0; i<len; i++){
		if(judge(s[i], a)){
			cnt+=(s[i].len-a.len+1LL);
		}
	}
	return cnt;
}
ll ans2(node *s, int len, node a, node b){//特判只有两块;
	ll cnt=0;
	for(int i=0; i<len-1; i++){
		if(judge(s[i], a)&&judge(s[i+1], b)) cnt++;
	}
	return cnt;
}
int len_s, len_t;
node s[maxn], t[maxn], q[maxn];
int main(){
	int n, m;
	while(~scanf("%d%d", &n, &m)){
		len_s=len_t=0;
		for(int i=0; i<n; i++){
			ll l;
			char r[5];
			scanf("%lld%s", &l, r);
			p.len=l;
			p.ch=r[1];
			if(len_t==0) t[len_t++]=p;
			else{
				if(p.ch==t[len_t-1].ch){
					t[len_t-1].len+=p.len;
				}
				else t[len_t++]=p;
			}
		}
		for(int i=0; i<m; i++){
			ll l;
			char r[5];
			scanf("%d%s", &l, r);
			p.len=l;
			p.ch=r[1];
			if(len_s==0) s[len_s++]=p;
			else{
				if(p.ch==s[len_s-1].ch){
					s[len_s-1].len+=p.len;
				}
				else s[len_s++]=p;
			}
		}
		node a, b;
		a=s[0], b=s[len_s-1];//令a, b分别为首尾块;
		int len_q=0;
		for(int i=1; i<len_s-1; i++){
			q[len_q++]=s[i];//把p初始化为中间块;
		}
		if(len_s>2)
			printf("%lld\n", KMP(t, len_t, q, len_q, a, b));
		else{
			if(len_s==1) printf("%lld\n", ans1(t, len_t, a));
			else{
				printf("%lld\n", ans2(t, len_t, a, b));
			}
		}
	}
	return 0;
} 
//9 5
//7-a 6-b 7-a 6-b 7-a 6-b 8-a 6-b 7-a
//7-a 6-b 7-a 6-b 7-a

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值