CF631D Messenger(KMP)
CF631D Messenger
Mean
给你两个字符串\(s,t\),求出\(t\)在\(s\)中出现了多少次。
这两个字符串可能会很长,所以字符串被分成很多块,其中\(s\)被分成\(n\)块,\(t\)被分成\(m\)块。每一块\((l,c)\)代表\(l\)个\(c\)字符连接在一起组成的字符串。即\((2,′a′)="aa"\)。一个字符串\(ss\)会被表示成一个序列\(((l1,c1),(l2,c2),…,(ln,cn))\)。在输入中字符串"l−c"代表\((l,c)\)。
注意到字符串的表示方式不是唯一的,例如\(((1,′a′),(3,′a′))=((2,′a′),(2,′a′))="aaaa"\)。
输入格式 第一行有两个整数\(n,m\);
第二行有\(n\)个块,表示字符串\(s\)。
第三行有\(m\)个块,表示字符串\(t\)。
输出格式 一行一个整数:答案。
Sol
KMP
因为表示方法不唯一,所以需要先将所有连续相同块合并起来。
对于\(lent=1与lent=2\)进行特判。
对于\(lent>=3\),可以观察到匹配成功时头尾不需要严格相同,除去头尾部分严格相同。
那么考虑对除去头尾的\(t\)数组求一个\(Next\),然后做一遍\(KMP\),最后\(check\)的时候再检查一下头尾即可。
Code
#include<bits/stdc++.h>
#define N 500005
using namespace std;
typedef long long ll;
struct node{
ll cnt;
char x;
void print(){
cout<<cnt<<" - "<<x<<endl;
}
}t[N],p[N],nt[N],np[N],rp[N];
int nlent,nlenp;
int next1[N];
ll ans=0;
void get_next(node p[],int lenp){
int j=0;
for(int i=2;i<=lenp;++i){
while(j&&(p[j+1].cnt!=p[i].cnt||p[j+1].x!=p[i].x))j=next1[j];
if(p[j+1].cnt==p[i].cnt&& p[j+1].x==p[i].x)j++;
next1[i]=j;
}
}
void kmp(node t[],int lent,node p[],int lenp){
int j=0;
for(int i=1;i<=lent;++i){
while(j&&(p[j+1].cnt!=t[i].cnt||p[j+1].x!=t[i].x))j=next1[j];
if(p[j+1].cnt==t[i].cnt&&p[j+1].x==t[i].x)j++;
if(j==lenp){
// cout<<"i="<<i<<endl;
if(np[1].cnt<=t[i-lenp].cnt&&np[1].x==t[i-lenp].x && np[nlenp].cnt<=t[i+1].cnt&&np[nlenp].x==t[i+1].x){
ans++;
}
//cout<<i-lenp+1<<endl;//输出p在t中出现的位置
}
}
}
void print(int lenp){
for(int i=1;i<=lenp;++i){
cout<<next1[i]<<" ";
}
}
int main(){
int lent,lenp;
scanf("%d %d",&lent,&lenp);
for(int i=1;i<=lent;++i){
scanf(" %d-%c",&t[i].cnt,&t[i].x);
}
for(int i=1;i<=lent;++i){
int pos = i;
ll sum = 0;
while(pos<=lent&&t[pos].x==t[i].x){
sum += t[pos].cnt;
pos++;
}
nt[++nlent].cnt = sum;
nt[nlent].x = t[i].x;
i=pos-1;
}
for(int i=1;i<=lenp;++i){
scanf(" %d-%c",&p[i].cnt,&p[i].x);
}
for(int i=1;i<=lenp;++i){
int pos = i;
ll sum = 0;
while(pos<=lenp&&p[pos].x==p[i].x){
sum += p[pos].cnt;
pos++;
}
np[++nlenp].cnt = sum;
np[nlenp].x = p[i].x;
i=pos-1;
}
if(nlenp==1){
for(int i=1;i<=nlent;++i){
if(np[1].cnt<=nt[i].cnt&&np[1].x==nt[i].x)ans+=nt[i].cnt-np[1].cnt+1;
}
}
else if(nlenp==2){
for(int i=1;i<=nlent-1;++i){
if(np[1].cnt<=nt[i].cnt&&np[1].x==nt[i].x&&np[2].cnt<=nt[i+1].cnt&&np[2].x==nt[i+1].x)ans++;
}
}
else{
int co=0;
for(int i=2;i<nlenp;++i){
rp[++co]=np[i];
}
get_next(rp,co);//预处理p的next数组
kmp(nt,nlent,rp,co);
}
printf("%lld\n",ans);
return 0;
}