CodeForces 471D MUH and Cube Walls KMP算法+串的取差性质

CodeForces 471D MUH and Cube Walls

现有一个包含 n 个整数的 a1, a2, ..., a序列和一个包含 m 个整数的 b1, b2, ..., bm

问你在 序列中有多少个不同连续子序列使得每个数同时加上某个整数(可以为负数)后和 b 序列相等?

两个子串不同当且仅当起始位置不同。

Input

第一行包含两个整数 n and m (1 ≤ n, m ≤ 2·105) — 表示两个序列的规模. 第二行包含 n 个整数 ai (1 ≤ ai ≤ 109) — 表示 数组. 第三行包含 m 个整数 bi (1 ≤ bi ≤ 109) — 表示 数组.

Output

输出连续子序列的个数.

Sample Input

  
13 5
2 4 5 5 4 3 2 2 2 3 3 2 1
3 4 4 3 2

Sample Output

  

2

Hint

在样例中,a 数组存在两个子序列(4 5 5 4 3)同时加上-1和(2 3 3 2 1)同时加上1后和 b 数组相同.



解题思路:

苦苦研究半天不知为何写不对,最后别人一丁点提醒瞬间明白了做法。

首先这是kmp算法,那么匹配的话不能直接进行,因为要考虑的情况太多了

那么就找串的性质了,匹配串和原串每一位和他前面的差都是固定,如果匹配,那么差值也会达到一致的效果

对于样例:

13 5
2 4 5 5 4 3 2 2 2 3 3 2 1
3 4 4 3 2

处理之后

12 4

2 1 0 -1 -1 -1 0 0 1 0 -1 -1

1 0 -1 -1

直接匹配即可获得结果,还要处理一下m=2的情况,处理完时候发现真简单。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 200005 ;
int n,m;
int f[maxn] ;
int fd[maxn] ;
int ans =0 ;
void find(int *T,int *P) {
    int j = 0 ;
    for(int i=0; i<n; i++) {
        while(j&&P[j]!=T[i])j = f[j] ;
        if(P[j]==T[i])j++ ;
        if(j==m) {
            ans++ ;
            j = f[j] ;
        }
    }
}
void getfail(int *p) { ///每一个数都可回到下标1位置,以后的匹配要满足误差d。
    f[0] = 0 ;
    f[1] = 0 ;
    for(int i=1; i<m; i++) {
        int j = f[i] ;
        while(j&&p[i]!=p[j])j = f[j] ;
        f[i+1] = (p[i]==p[j] ? j+1 : 0) ;
    }
}
int str[maxn] ;
int p[maxn] ;
int main() {
    //freopen("in.txt","r",stdin) ;
    while(~scanf("%d%d",&n,&m)) {
        ans = 0 ;
        for(int i=0; i<n; i++) scanf("%d",&str[i]) ;
        for(int i=0; i<m; i++) scanf("%d",&p[i]) ;
        if(m>n) {
            printf("0\n") ;
            continue ;
        }
        if(m==1) {
            printf("%d\n",n) ;
            continue ;
        }
        for(int i=0;i<n-1;i++)str[i] = str[i+1]-str[i] ;
        for(int i=0;i<m-1;i++)p[i] = p[i+1]-p[i] ;
        m-- ;n--;
        getfail(p);
        find(str,p) ;
        printf("%d\n",ans);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值