題目大意:給出字符串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;
}