BZOJ_4892_[Tjoi2017]dna_哈希
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
分析:
枚举S0中每个与S等长的子串。我们求三次S和当前子串的lcp(最长公共前缀)来确定S0是否能够修改小于等于3个字母变成S。
lcp求法:二分加哈希
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 100050 #define LL unsigned long long int T, l1, l2; char s1[N], s2[N]; LL mi[N], p = 19260817, h1[N], h2[N]; bool check(int x) { int i = x, j = 1; int l = 1, r = l2 + 1, mid; for(int k = 1;k <= 3; ++ k) { while(l < r) { mid = (l + r) >> 1; if(h1[i + mid - 1] - h1[i - 1] * mi[mid] == h2[j + mid - 1] - h2[j - 1] * mi[mid]) l = mid + 1; else r = mid; } i = i + l; j = j + l; l = 1, r = l2 - j + 2; if(j > l2) return 1; mid = l2 - j + 1; if(h1[i + mid - 1] - h1[i - 1] * mi[mid] == h2[j + mid - 1] - h2[j - 1] * mi[mid]) return 1; } return 0; } int main() { scanf("%d", &T); while(T -- ) { int i, ans = 0; scanf("%s%s", s1 + 1, s2 + 1); l1 = strlen(s1 + 1); l2 = strlen(s2 + 1); if(l1 < l2) { puts("0"); continue; } mi[0] = 1; for(i = 1;i <= l1; ++ i) { mi[i] = mi[i - 1] * p; h1[i] = h1[i - 1] * p + s1[i]; } for(i = 1;i <= l2; ++ i) h2[i] = h2[i - 1] * p + s2[i]; for(i = 1;i <= l1 - l2 + 1; ++ i) { if(check(i)) ans++; } printf("%d ", ans); } }