🎈文章目录
🎉🎉🎉🎉号外号外高校算法学习社区开始新活动啦🚩🚩🚩因为同学们的基础不一样,觉得原来的每日一题比较简单,所以我们决定开设一个普及组一个提高组🙇🙇🙇提高组由我负责,每日一题,我们一起卷起来🚀🚀🚀
🌴题目描述
栗酱有一个长度为n的数列A,一个长度为m的数列B,现在询问A中有多少个长度为m的连续子序列A’,满足 ( a ′ 1 + b 1 ) % k = ( a ′ 2 + b 2 ) % k = … … = ( a ′ m + b m ) % k (a'1+b1)\%k = (a'2+b2)\%k = …… = (a'm + bm)\%k (a′1+b1)%k=(a′2+b2)%k=……=(a′m+bm)%k。
🌃解题报告
1.通过差分预处理来化解式子 |
我们看到这题时候首先能看到一个奇怪的式子,我们先要想办法把他弄得好看一点。
(
a
1
+
b
1
)
%
k
=
=
(
a
2
+
b
2
)
%
k
(a1+b1)\%k==(a2+b2)\%k
(a1+b1)%k==(a2+b2)%k我们可以转化为
(
a
1
−
a
2
+
b
1
−
b
2
)
%
k
=
=
0
(a1-a2+b1-b2)\%k==0
(a1−a2+b1−b2)%k==0但是对于照相等子序列的问题我们一般是对数组做的,所以我们可以把
a
1
−
a
2
a1-a2
a1−a2看成一项,也就是预处理一个差分数组。预处理差分数组以后我们就可以把这一道题转化成:
在一个长度为n的数列A'中寻找长度为m的序列B'作为子序列一共出现了多少次
2.通过kmp求解答案 |
转化之后就清楚多了,就是一个kmp的模板题,我们就可以用kmp算法求解出这个问题。只需要注意到这道题的相等关系是在模k意义下的,也就是同余,所以匹配时的相等要写成差模k等于0。
🍬AC代码(c++)
#include<iostream>
#define endl "\n"
#define int long long
using namespace std;
const int N=2e5+10;
int ne[N];
int a[N],b[N];
int doa[N],dob[N];
void run() {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
}
signed main() {
run();
int t;
cin>>t;
while(t--) {
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
for(int i=1;i<n;i++) doa[i]=(a[i+1]%k-a[i]%k+k)%k;
for(int i=1;i<m;i++) dob[i]=(b[i+1]%k-b[i]%k+k)%k;
ne[1]=0;
for(int i=2;i<m;i++) {
ne[i]=ne[i-1];
while(ne[i]&&dob[ne[i]+1]!=dob[i]) ne[i]=ne[ne[i]];
if(dob[ne[i]+1]==dob[i]) ne[i]++;
}
int ans=0;
for(int i=1,j=0;i<n;i++) {
while(j&&(dob[j+1]+doa[i])%k) j=ne[j];
if((dob[j+1]+doa[i])%k==0) j++;
if(j==m-1) {
ans++;
j=ne[j];
}
}
cout<<ans<<endl;
}
}