LA4671 K-neighbor substrings(FFT + 字符串Hash)
题目
Source
http://acm.hust.edu.cn/vjudge/problem/19225
Description
The Hamming distance between two strings of the same length is defined as the number of positions at which the corresponding characters are different. For example, the Hamming distance between “abbab” and “bbabb” is 3.
A string is called a K-neighbor of another string if and only if they are of the same length and the Hamming distance between them is not larger than K. In this problem, given an integer K and two strings A and B which only contain character ‘a’ and ‘b’, you are to count how many different sub-strings of A are K-neighbors of B.
Input
The input consists of multiple test cases. Each test case starts with a line containing one integer K (0 ≤ K ≤ 100, 000).
The following two lines give two non-empty strings consisting of ‘a’ and ‘b’, which are string A and string B, respectively.
The length of strings A and B will both lie between 1 and 100,000, inclusive. The last test case is followed by a line containing one ‘-1’.
Output
For each test case, print a line containing the test case number (beginning with 1) followed by the number of different sub-strings of string A which are K-neighbors of string B.
Sample Input
0
aabbab
ab
1
aabbab
ab
2
aabba
ab
-1
Sample Output
Case 1: 1
Case 2: 3
Case 3: 4
分析
题目大概说给两个由'a'和'b'组成的字符串A和B,问A有几个不同的子串满足其长度等于B且与B各个对应位置不同的字符总数不超过k。
颠倒数组转化成可以用FFT解决的卷积形式。。原来是这么个意思。
http://blog.csdn.net/pure_lady/article/details/48749635
假设len(B)=n,将B翻转,然后A与B对应位置就是A[x]与B[n-1]、A[x+1]与B[n-2]、...、A[x+n-1]与B[0],可以发现(x)+(n-1) = (x+1)+(n-2) = ... = (x+n-1)+(0),类似多项式乘法。
然后如果把'a'作为1,'b'作为0,字符串各个位置作为指数,表示出A和反转的B的两个多项式,用FFT求出乘积,即类似这种ΣA(i)B(n-i)卷积形式,最后得出这个乘积的结果中指数为x+(n-1)(0<=x<len(A)-(n-1))的系数就表示A中以x+(n-1)结尾的子串有多少个'a'与B串对应位置相同;
把'b'作为1,'a'作为0作同样处理即可同样得出A中各个子串'b'对应位置相同的个数。
感觉还是挺神奇的。。
这样不同个数不超过k就相当于相同个数大于等于len(B)-k。不过问题要求的是不同的子串,还要去掉重复的。。
可以用字符串Hash做。。字符串Hash我是乱搞的,改了几个值,改了模数,又尝试了双重hash。。WA又TLE十多次后,最后终于AC了。。
其实我不会字符串Hash,全都是乱搞的= =。。
不过后缀数组也是可以做的,本来要放弃改用后缀数组的,没想到居然AC了。。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<set> #include<algorithm> using namespace std; #define MAXN 277777 #define MOD 9999999967LL const double PI=acos(-1.0); struct Complex{ double real,imag; Complex(double _real,double _imag):real(_real),imag(_imag){} Complex(){} Complex operator+(const Complex &cp) const{ return Complex(real+cp.real,imag+cp.imag); } Complex operator-(const Complex &cp) const{ return Complex(real-cp.real,imag-cp.imag); } Complex operator*(const Complex &cp) const{ return Complex(real*cp.real-imag*cp.imag,real*cp.imag+cp.real*imag); } void setValue(double _real=0,double _imag=0){ real=_real; imag=_imag; } }; int len; Complex wn[MAXN],wn_anti[MAXN]; void FFT(Complex y[],int op){ for(int i=1,j=len>>1,k; i<len-1; ++i){ if(i<j) swap(y[i],y[j]); k=len>>1; while(j>=k){ j-=k; k>>=1; } if(j<k) j+=k; } for(int h=2; h<=len; h<<=1){ Complex Wn=(op==1?wn[h]:wn_anti[h]); for(int i=0; i<len; i+=h){ Complex W(1,0); for(int j=i; j<i+(h>>1); ++j){ Complex u=y[j],t=W*y[j+(h>>1)]; y[j]=u+t; y[j+(h>>1)]=u-t; W=W*Wn; } } } if(op==-1){ for(int i=0; i<len; ++i) y[i].real/=len; } } void Convolution(Complex A[],Complex B[],int n){ for(len=1; len<(n<<1); len<<=1); for(int i=n; i<len; ++i){ A[i].setValue(); B[i].setValue(); } FFT(A,1); FFT(B,1); for(int i=0; i<len; ++i){ A[i]=A[i]*B[i]; } FFT(A,-1); } char s1[111111],s2[111111]; int cnt[MAXN]; Complex A[MAXN],B[MAXN]; int main(){ for(int i=0; i<277777; ++i){ wn[i].setValue(cos(2.0*PI/i),sin(2.0*PI/i)); wn_anti[i].setValue(wn[i].real,-wn[i].imag); } int k,cse=0; while(scanf("%d",&k)==1 && k!=-1){ scanf("%s%s",s1,s2); int n1=strlen(s1),n2=strlen(s2); if(n1<n2){ printf("Case %d: 0\n",++cse); continue; } for(int i=0; i<n1; ++i){ if(s1[i]=='a') A[i].setValue(1); else A[i].setValue(0); } for(int i=0; i<n2; ++i){ if(s2[i]=='a') B[n2-i-1].setValue(1); else B[n2-i-1].setValue(0); } for(int i=n2; i<n1; ++i){ B[i].setValue(0); } Convolution(A,B,n1); for(int i=0; i<len; ++i){ cnt[i]=(int)(A[i].real+0.5); } for(int i=0; i<n1; ++i){ if(s1[i]=='b') A[i].setValue(1); else A[i].setValue(0); } for(int i=0; i<n2; ++i){ if(s2[i]=='b') B[n2-i-1].setValue(1); else B[n2-i-1].setValue(0); } for(int i=n2; i<n1; ++i){ B[i].setValue(0); } Convolution(A,B,n1); for(int i=0; i<len; ++i){ cnt[i]+=(int)(A[i].real+0.5); } k=n2-k; int tot=0; long long h=0,mi=1; set<long long> hash; for(int i=0; i<n2-1; ++i){ h=(h*8887+s1[i]*3)%MOD; mi*=8887; mi%=MOD; } for(int i=n2-1; i<n1; ++i){ h=(h*8887+s1[i]*3)%MOD; if(hash.count(h)==0){ if(cnt[i]>=k) ++tot; hash.insert(h); } h-=(s1[i-n2+1]*3*mi)%MOD; h%=MOD; if(h<0) h+=MOD; } printf("Case %d: %d\n",++cse,tot); } return 0; }