题目大意:给出字符串A和字符串B,现在要求出字符串A有多少个长度与字符串B的长度相同且只有不超过k个位置上的字符不同的子串。
题目思路:本题是FFT进行字符串匹配的一个入门题。题目中要不超过k个位置上的字符不同的子串,那么问题就可以转换为求大于|B|-k个位置上的字符相同的子串。
由于字符串A和B只有 'a' 和 'b' 这两种字符,我们就可以对字符串A进行转化,令每一位为'a'的值设为1,,‘b’的值设为0,然后把字符串B进行翻转,再进行同样的转化,接着进行多项式乘法,就能求出各个子串的匹配情况,只有对应位置都为 'a' 求出的多项式对应值才会为1。同理,我们再令字符 'b' 的值为1,字符 'a' 的值为0,我们就可以借助FFT来求多项式乘法的卷积来解决。最后再用一个字符串hash去下重即可。
解释下为什么要将字符串B翻转。因为在做多项式乘法的时候,下标和相同的乘积结果会被放在结果的多项式下标相同的位置,比如C为多项式A和B相乘之后所得到的结果,那么C[3]=A[0]*B[3]+A[1]*B[2]+A[2]*B[1]+A[3]+B[0]。现在将B翻转之后再进行相乘,所得到的多项式的第 i 项就表示字符串A中以 i 为结尾长度为 |B| 的子串与字符串B的匹配情况。这样就可以O(n)枚举出所有子串的情况了。
具体实现看代码:
#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lowbit(x) x&-x
#define pb push_back
#define MP make_pair
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define fuck(x) cout<<"["<<x<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
//head
const int MX=1e5+7;
const int has = 2333;
int k;
char s1[MX],s2[MX];
ull H[MX],P[MX];
unordered_map<ull,int>mp;
void Hash(char str[],int n){
H[0]=0;P[0]=1;
for(int i=1;i<=n;i++){
H[i]=H[i-1]*has+str[i-1]-'a'+1;
P[i]=P[i-1]*has;
}
}
ull get_hash(int l,int r){
return H[r]-H[l-1]*P[r-l+1];
}
const double pi = acos(-1.0);
int len,mx,res1[MX<<2],res2[MX<<2];
struct Complex {
double r,i;
Complex(double r=0,double i=0):r(r),i(i) {};
Complex operator+(const Complex &rhs) {return Complex(r + rhs.r,i + rhs.i);}
Complex operator-(const Complex &rhs) {return Complex(r - rhs.r,i - rhs.i);}
Complex operator*(const Complex &rhs) {return Complex(r*rhs.r - i*rhs.i,i*rhs.r + r*rhs.i);}
} va[MX<<2],vb[MX<<2];
void rader(Complex F[],int len) { //len = 2^M,reverse F[i] with F[j] j为i二进制反转
int j = len >> 1;
for(int i = 1; i < len - 1; ++i) {
if(i < j) swap(F[i],F[j]); // reverse
int k = len>>1;
while(j>=k) {
j -= k;
k >>= 1;
}
if(j < k) j += k;
}
}
void FFT(Complex F[],int len,int t) {
rader(F,len);
for(int h=2; h<=len; h<<=1) {
Complex wn(cos(-t*2*pi/h),sin(-t*2*pi/h));
for(int j=0; j<len; j+=h) {
Complex E(1,0); //旋转因子
for(int k=j; k<j+h/2; ++k) {
Complex u = F[k];
Complex v = E*F[k+h/2];
F[k] = u+v;
F[k+h/2] = u-v;
E=E*wn;
}
}
}
if(t==-1) //IDFT
for(int i=0; i<len; ++i)
F[i].r/=len;
}
void Conv(Complex a[],Complex b[],int len) { //求卷积
FFT(a,len,1);
FFT(b,len,1);
for(int i=0; i<len; ++i) a[i] = a[i]*b[i];
FFT(a,len,-1);
}
void work(int x[]) {
Conv(va,vb,len);
for(int i=0; i<len; ++i) x[i]=va[i].r + 0.5;
}
int main(){
//FIN;
int cas=1;
while(~scanf("%d",&k) && k!=-1){
scanf("%s%s",s1,s2);
int len1=strlen(s1),len2=strlen(s2);
if(len1<len2){
printf("Case %d: 0\n",cas++);
continue;
}
mx=len1+1;
len=1;
while(len<2*mx) len<<=1;
for(int i=0;i<len;i++){
if(i<len1) va[i]=Complex(s1[i]=='a'?1:0,0);
else va[i]=Complex(0,0);
if(i<len2) vb[i]=Complex(s2[len2-i-1]=='a'?1:0,0);
else vb[i]=Complex(0,0);
}
work(res1);
for(int i=0;i<len;i++){
if(i<len1) va[i]=Complex(s1[i]=='b'?1:0,0);
else va[i]=Complex(0,0);
if(i<len2) vb[i]=Complex(s2[len2-i-1]=='b'?1:0,0);
else vb[i]=Complex(0,0);
}
work(res2);
Hash(s1,len1);mp.clear();
int ans=0;
for(int i=len2-1;i<len1;i++){
if(len2-res1[i]-res2[i]<=k){
ull h=get_hash(i-len2+2,i+1);
if(mp[h]==0){
mp[h]=1;
ans++;
}
}
}
printf("Case %d: %d\n",cas++,ans);
}
return 0;
}