631D Messenger

题目大意

给你串s和t

但是每个串都被表示为多个二元组(x,y)表示字符x连续出现y次

问t在s中出现了多少次

分析

我们先将s和t每个串中二元组合并

即相邻两个二元组如果字符相等则将它们变为一个

特判掉m=1的情况

其余情况我们发现对于相等位置除了t的开头结尾两个二元组

其余二元组一定与和s的对应位置完全一样

我们把t去掉头尾放在前面

然后将s放在后面

求出它们的z函数

之后对于s的每个位置如果它的z[i]大于等于m-2

且它的两端字符和t两端相等且个数不小于

那么这个位置合法

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
#define int long long
char l[500100],ls[500100],lt[500100];
int c[500100],cs[500100],ct[500100],n,m,z[500100],ans,N;
inline void get_z(){
    int i,j,k,le=0,ri=0;
    z[0]=N;
    for(i=1;i<N;i++){
      if(i<=ri)z[i]=min(ri-i+1,z[i-le]);
      while(z[i]+i<N&&l[z[i]]==l[z[i]+i]&&c[z[i]]==c[z[i]+i])z[i]++;
      if(z[i]+i-1>ri)ri=z[i]+i-1,le=i;
    }
}
signed main(){
    int i,j,k;
    scanf("%lld%lld",&n,&m);
    for(i=0;i<n;i++)scanf("%lld-%c",&cs[i],&ls[i]);
    N=0;
    for(i=1;i<n;i++)
      if(ls[i]==ls[i-1])cs[N]+=cs[i];
        else ls[++N]=ls[i],cs[N]=cs[i];
    n=N+1;
    for(i=0;i<m;i++)scanf("%lld-%c",&ct[i],&lt[i]);
    N=0;
    for(i=1;i<m;i++)
      if(lt[i]==lt[i-1])ct[N]+=ct[i];
        else lt[++N]=lt[i],ct[N]=ct[i];
    m=N+1;
    if(m==1){
      for(i=0;i<n;i++)
        if(ls[i]==lt[0]&&cs[i]>=ct[0])ans+=cs[i]-ct[0]+1;
      printf("%lld\n",ans);
      return 0;
    }
    N=n+m-2;
    for(i=1;i<m-1;i++)l[i-1]=lt[i],c[i-1]=ct[i];
    for(i=0;i<n;i++)l[m-2+i]=ls[i],c[m-2+i]=cs[i];
    get_z();
    for(i=m-1;i<N-m+2;i++)
      if(z[i]>=m-2&&l[i-1]==lt[0]&&l[i+m-2]==lt[m-1]&&c[i-1]>=ct[0]&&c[i+m-2]>=ct[m-1])ans++;
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-09-01 09:04  水题收割者  阅读(223)  评论(0编辑  收藏  举报