BZOJ_4892_[Tjoi2017]dna_哈希

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

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);
	}
}
 
原文地址:https://www.cnblogs.com/suika/p/8672067.html