链接:https://ac.nowcoder.com/acm/problem/14694
来源:牛客网
题目描述
栗酱有一个长度为n的数列A,一个长度为m的数列B,现在询问A中有多少个长度为m的连续子序列A',
满足(a'1+b1)%k = (a'2+b2)%k = …… = (a'm + bm)%k。
输入描述:
第一行一个数T,表示有T组数据。
对于每组数据,
第一行三个整数,n, m, k。
第一行输入n个数, a1,a2,…,an, 表示A数列中的数,
第二行输入m个数, b1,b2,…,bm, 表示B数列中的数。
输出描述:
每一组数据输出一行,满足条件的连续子序列数量。
示例1
输入
复制2 3 2 5 7 8 7 8 7 3 2 5 7 8 9 8 7
2 3 2 5 7 8 7 8 7 3 2 5 7 8 9 8 7
输出
复制1 2
1 2
备注:
T≤15,
2≤m≤n≤2×105,
1≤ai,bi,k≤109
思路
匹配字串,将kmp的匹配规则字符串相等改成取模等于0。至于怎么改就需要看这个式子怎么化简了。
用a1,a2,b1,b2为例,(a1+b1)%k=(a2+b2)%k
即是(a1+b1)%k-(a2+b2)%k=0,
即是(a1+b1-a2-b2)%k=0,
即是(a1-a2+b1-b2)%k=0,
即是(a1-a2)%k+(b1-b2)%k=0。
代码里 al[i]=((a[i] - a[i+1]) % k + k) % k 的这个%k+k)%k是因为相减的结果可能是负的,所以先处理一下后面再取模就不用担心数是负数了。
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,k,a[200005],b[200005];
int al[200005],bl[200005],nexttable[200005];
void getnext(){
int j=0,len=-1;
nexttable[0]=-1;
while(j<m-1){
if(bl[j]==bl[len]||len==-1){
len++;j++;
nexttable[j]=len;
}else{
len=nexttable[len];
}
}
}
int kmp(){
int i=0,j=0,count=0;
while(i<n-1&&j<m-1){
if((al[i]+bl[j])%k==0||j==-1){
i++;j++;
}else{
j=nexttable[j];
}
if(j==m-1){
count++;
j=nexttable[j];
}
}
return count;
}
int main(){
int T;
cin>>T;
while(T--){
cin>>n>>m>>k;
for(int i=0;i<n;i++){
cin>>a[i];
}
for(int i=0;i<n-1;i++){
al[i]=((a[i] - a[i+1]) % k + k) % k;
}
for(int i=0;i<m;i++){
cin>>b[i];
}
for(int i=0;i<m-1;i++){
bl[i]=((b[i] - b[i + 1]) % k + k) % k;
}
getnext();
cout << kmp() <<endl;
}
return 0;
}