CodeForces 471D MUH and Cube Walls
现有一个包含 n 个整数的 a1, a2, ..., an 序列和一个包含 m 个整数的 b1, b2, ..., bm.
问你在 a 序列中有多少个不同连续子序列使得每个数同时加上某个整数(可以为负数)后和 b 序列相等?
两个子串不同当且仅当起始位置不同。
Input
第一行包含两个整数 n and m (1 ≤ n, m ≤ 2·105) — 表示两个序列的规模. 第二行包含 n 个整数 ai (1 ≤ ai ≤ 109) — 表示 a 数组. 第三行包含 m 个整数 bi (1 ≤ bi ≤ 109) — 表示 b 数组.
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;
}