BZOJ4892:[TJOI2017]dna(hash)
Description
加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状。现在研究人员想知道这个基因在DNA链S0上的位置。所以你需要统计在一个表现出吃藕性状的人的DNA序列S0上,有多少个连续子串可能是该基因,即有多少个S0的连续子串修改小于等于三个字母能够变成S。
Input
第一行有一个数T,表示有几组数据 每组数据第一行一个长度不超过10^5的碱基序列S0
每组数据第二行一个长度不超过10^5的吃藕基因序列S
Output
共T行,第i行表示第i组数据中,在S0中有多少个与S等长的连续子串可能是表现吃藕性状的碱基序列
Sample Input
1
ATCGCCCTA
CTTCA
ATCGCCCTA
CTTCA
Sample Output
2
Solution
以$S_0$的每一位为开头和$S$进行匹配,用$hash$求$lcp$然后往后蹦。
因为题目限制所以我们只会往后蹦常数次。
记得预处理区间$hash$要用的快速幂,复杂度$O(nlogn)$。
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define N (100009) 5 #define LL unsigned long long 6 using namespace std; 7 8 int T,l1,l2,ans; 9 LL h1[N],h2[N],Q[N]; 10 char s[N],t[N]; 11 12 LL Qpow(LL a,int b) 13 { 14 LL ans=1; 15 while (b) 16 { 17 if (b&1) ans=ans*a; 18 a=a*a; b>>=1; 19 } 20 return ans; 21 } 22 23 LL gethash(int opt,int x,int y) 24 { 25 if (opt==1) return h1[y]-h1[x-1]*Q[y-x+1]; 26 else return h2[y]-h2[x-1]*Q[y-x+1]; 27 } 28 29 int getlcp(int x,int y) 30 { 31 int l=1,r=min(l1-x+1,l2-y+1),ans=0; 32 while (l<=r) 33 { 34 int mid=(l+r)>>1; 35 if (gethash(1,x,x+mid-1)==gethash(2,y,y+mid-1)) ans=mid,l=mid+1; 36 else r=mid-1; 37 } 38 return ans; 39 } 40 41 int main() 42 { 43 scanf("%d",&T); 44 for (int i=1; i<=100000; ++i) Q[i]=Qpow((LL)100007,i); 45 while (T--) 46 { 47 ans=0; 48 scanf("%s%s",s+1,t+1); 49 l1=strlen(s+1), l2=strlen(t+1); 50 for (int i=1; i<=l1; ++i) h1[i]=h1[i-1]*100007+s[i]-'A'+1; 51 for (int i=1; i<=l2; ++i) h2[i]=h2[i-1]*100007+t[i]-'A'+1; 52 for (int i=1; i<=l1-l2+1; ++i) 53 { 54 int cnt=0; 55 for (int j=1; j<=l2 && cnt<=3;) 56 if (s[i+j-1]!=t[j]) ++cnt, ++j; 57 else j+=getlcp(i+j-1,j); 58 if (cnt<=3) ans++; 59 } 60 printf("%d\n",ans); 61 } 62 }