Codeforces Round #344 (Div. 2) D. Messenger(kmp)

题目链接:点这里!!!!


题意:

给你两个字符串a,b,求b在a中出现的次数。

字符串出现的形式为a1-c1 a2-c2 a3-c3 ... an-cn(b字符串为m)。ai-ci表示ci这个字符连续出现了ai次。

比如:3-a 2-b 2-a 3-d 是aaabbaaddd。

取值范围:m,n<=200000, ai<=1e6


题解:

1、如果出现3-a 7-a这种相邻两个的字符是一样的,先合并掉。

2、m==1我们直接去找a中哪一块的字符为与b中的相等,然后去判个数就可以了。

3、m==2我们考虑a中相邻的两个字符是否与b中的两个相等,再判下每个块的大小关系就可以了。

4、m>2 去掉b的头部和尾部,直接去a中跑kmp就可以了。如果匹配到了的话,再判下两头是否符合条件就可以了。


代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<sstream>
#include<algorithm>
#include<vector>
#include<bitset>
#include<set>
#include<queue>
#include<stack>
#include<map>
#include<cstdlib>
#include<cmath>
#define LL long long
#define pb push_back
#define pa pair<int,int>
#define clr(a,b) memset(a,b,sizeof(a))
#define lson lr<<1,l,mid
#define rson lr<<1|1,mid+1,r
#define bug(x) printf("%d++++++++++++++++++++%d\n",x,x)
#define key_value ch[ch[root][1]][0]
#pragma comment(linker, "/STACK:102400000000,102400000000")
const LL  MOD = 1000000007;
const int N = 2e5+15;
const int maxn = 8e3+15;
const int letter = 130;
const LL INF = 1e7;
const double pi=acos(-1.0);
const double eps=1e-10;
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,f[N];
LL ans=0;
struct node{
    LL v;
    char c;
    bool operator ==(const node &p)const {
        return v==p.v&&c==p.c;
    }
}a[N],b[N],p[N];
void getf(node a[],int n){
    f[0]=f[1]=0;
    int j=0;
    for(int i=1;i<n;i++){
        j=f[i];
        while(j&&!(a[i]==a[j])) j=f[j];
        if(a[i]==a[j]) f[i+1]=j+1;
        else f[i+1]=0;
    }
}
void kmp(node a[],node b[]){
    for(int i=1;i<m-1;i++) p[i-1]=b[i];
    getf(p,m-2);
    int vs=m-2;
    for(int i=1,j=0;i<n-1;i++){
        if(j==vs) j=f[j];
        while(j&&!(a[i]==p[j]))j=f[j];
        if(a[i]==p[j])j++;
        if(j==vs){
            if(b[0].c==a[i-vs].c&&b[0].v<=a[i-vs].v&&b[m-1].c==a[i+1].c&&b[m-1].v<=a[i+1].v)ans++;
        }
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++) scanf("%I64d-%c",&a[i].v,&a[i].c);
    for(int i=0;i<m;i++) scanf("%I64d-%c",&b[i].v,&b[i].c);
    int cnt=0;
    for(int i=1;i<n;i++){
        if(a[i].c!=a[i-1].c) a[++cnt]=a[i];
        else a[cnt].v+=a[i].v;
    }
    n=cnt+1;
    cnt=0;
    for(int i=1;i<m;i++){
        if(b[i].c!=b[i-1].c) b[++cnt]=b[i];
        else b[cnt].v+=b[i].v;
    }
    m=cnt+1;
    if(m==1){
        for(int i=0;i<n;i++){
            if(a[i].c==b[0].c&&a[i].v>=b[0].v) ans+=a[i].v-b[0].v+1;
        }
    }
    else if(m==2){
        for(int i=0;i<n-1;i++){
            if(a[i].c==b[0].c&&a[i+1].c==b[1].c&&a[i].v>=b[0].v&&a[i+1].v>=b[1].v)ans++;
        }
    }
    else {
        kmp(a,b);
    }
    printf("%I64d\n",ans);
    return 0;
}
/**
10 8
1-a 1-b 1-a 1-b 1-a 1-b 1-a 1-b 1-a 1-b
1-a 1-b 1-a 1-b 1-a 1-b 1-a 1-b
**/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值