problem
给出长度为 n n n 的串 S S S 和长度为 m m m 的串 T T T。
定义串 A A A 与串 B B B 匹配为, A A A 做若干次变换(例如对于 A = 12321 A=12321 A=12321,交换 1 1 1 和 2 2 2 变成 21312 21312 21312)后可以得到 B B B。
求出 S S S 中有多少长度为 m m m 子串 S ( i , i + m − 1 ) S(i,i+m-1) S(i,i+m−1) 可以与 T T T 匹配,并求出匹配的起始位置。
数据范围: n , m , C ≤ 1 0 6 n,m,C\le10^6 n,m,C≤106,其中 C C C 是字符集大小。
solution
这题其实挺简单的。
根据题目新定义的匹配的特性,我们把每个字符设成上一个与它相等的字符到它的距离,然后做 KMP 即可。
有一个细节就是把 B 中每一个字符第一个出现位置设成 − 1 -1 −1,那么 A 中任意一个字符都可以与它匹配。
时间复杂度 O ( n ) O(n) O(n)。
code
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
int n,m,a[N],b[N],A[N],B[N],last[N],nxt[N],ans[N];
void solve(int *A,int *a){
memset(last,-1,sizeof(last));
for(int i=1;i<=n;++i) A[i]=(last[a[i]]==-1)?-1:i-last[a[i]],last[a[i]]=i;
}
bool match(int x,int y){
if(B[x]==-1){
if(y>=x||y==-1) return true;
}
return B[x]==y;
}
int main(){
int T,C;
scanf("%d%d",&T,&C);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=m;++i) scanf("%d",&b[i]);
solve(A,a),solve(B,b);
for(int i=2,j=0;i<=m;++i){
while(j&&!match(j+1,B[i])) j=nxt[j];
if(match(j+1,B[i])) ++j;
nxt[i]=j;
}
int tot=0;
for(int i=1,j=0;i<=n;++i){
while(j&&!match(j+1,A[i])) j=nxt[j];
if(match(j+1,A[i])) ++j;
if(j==m) ans[++tot]=i-m+1,j=nxt[j];
}
printf("%d\n",tot);
for(int i=1;i<=tot;++i) printf("%d ",ans[i]);
puts("");
}
return 0;
}