UVALive - 4671 K-neighbor substrings (FFT+hash)

題目鏈接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2672

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章