bzoj4892 [TJOI2017]DNA

\(\verb|bzoj4892 [TJOI2017]DNA|\)

给定一个匹配串和一个模式串,求模式串有多少个连续子串能够修改不超过 \(3\) 个字符变成匹配串

\(len\leq10^5\)

hash



枚举子串左端点,hash 求 lcp 枚举断点,接着跳过断点,记作一次修改,最多修改 \(3\) 次。特判修改 \(3\) 次后剩余部分是否相等。

时间复杂度 \(O(n\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
const int maxn = 1e5 + 10;
int Tests;
int n, m;
char str[maxn], s[maxn];
ull pw[maxn], sum1[maxn], sum2[maxn];

ull getsum(ull* a, int l, int r) {
  return a[r] - a[l - 1] * pw[r - l + 1];
}

int query(int x, int y) {
  int l = 0, r = m - y + 1, mid;
  while (l < r) {
    mid = (l + r + 1) >> 1;
    getsum(sum1, x, x + mid - 1) == getsum(sum2, y, y + mid - 1) ? l = mid : r = mid - 1;
  }
  return r;
}

int check(int x) {
  int y = 1;
  for (int i = 0; i < 3; i++) {
    int tmp = query(x, y);
    x += tmp + 1, y += tmp + 1;
    if (y > m) return 1;
  }
  return getsum(sum1, x, x + m - y) == getsum(sum2, y, m);
}

void solve() {
  scanf("%s %s", str + 1, s + 1);
  n = strlen(str + 1), m = strlen(s + 1);
  for (int i = 1; i <= n; i++) {
    sum1[i] = sum1[i - 1] * 131 + str[i];
  }
  for (int i = 1; i <= m; i++) {
    sum2[i] = sum2[i - 1] * 131 + s[i];
  }
  int ans = 0;
  for (int i = 1; i <= n - m + 1; i++) {
    ans += check(i);
  }
  printf("%d\n", ans);
}

int main() {
  pw[0] = 1;
  for (int i = 1; i < 100001; i++) {
    pw[i] = pw[i - 1] * 131;
  }
  scanf("%d", &Tests);
  while (Tests--) {
    solve();
  }
  return 0;
}

原文地址:https://www.cnblogs.com/Juanzhang/p/10661403.html

时间: 2024-08-01 12:32:35

bzoj4892 [TJOI2017]DNA的相关文章

[BZOJ4892][TJOI2017]DNA(后缀数组)

题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状.现在研究人员想知道这个基因在DNA链S0上的位置.所以你需要统计在一个表现出吃藕性状的人的DNA序列S0上,有多少个连续子串可能是该基因,即有多少个S0的连续子串修改小于等于三个字母能够变成S. 输入输出格式 输入格式: 第一行有一个数T,表示有几组数据 每组数据第一行一个长度不超过10^5的碱基

[TJOI2017]DNA --- 后缀数组

[TJOI2017]DNA 题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S, 有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状. 现在研究人员想知道这个基因在DNA链\(S_{0}\)上的位置. 所以你需要统计在一个表现出吃藕性状的人的DNA序列\(S_{0}\)上,有多少个子串可能是该基因, 即有多少个\(S_{0}\)的子串修改小于等于三个字母能够变成S. 输入输出格式 输入格式: 第一行

luogu题解 P3763 【[TJOI2017]DNA】

题目链接: https://www.luogu.org/problemnew/show/P3763 思路: 首先我们要用到Rabin-Karp哈希,其实就是这个: 若\(w_{str}\)=(\(a_0\) \(p^{n-1}\)+\(a_1\) \(p^{n-2}\)+...+\(a_{n-1}\) \(p^0\)) 所以 \(w_{pre_{i-1}}\) \(=(\) \(a_0\) \(p^{i-1}\)+\(a_1\) \(p^{i-2}\)+...+\(a_{i-1}\) \(p^0

[TJOI2017] DNA 解题报告 (hash+二分)

题目链接:https://www.luogu.org/problemnew/show/P3763 题目大意: 给定原串S0,询问S0有多少个子串和给定串S相差不到3个字母 题解: 我们枚举S0的子串,问题转化为如何高效的判断两个串是否相差不到三个字母 考虑到数据范围,发现只能有log的时间余地 自然想到二分 solve每次找到第一个不同的位置并且返回,连续solve 4次,若S在这期间被处理完了,那么说明两个串相差不到3个字母 当然还有一些小细节 #include<cstdio> #inclu

[TJOI2017]DNA——后缀数组求LCP

题目大意: 给定一个文本串和一个模式串,求文本串中有多少个连续的子串和模式串相差不超过三个字符. 思路: 算是一道后缀数组的模板题. 直接做lcp,然后遇到匹配不上的就跳,跳的次数不能超过三次. 具体地,将两个字符串连在一起,中间加一个分隔符,然后求出height,用rmq维护height数组的区间最小值即可. /*======================================= * Author : ylsoi * Time : 2019.2.5 * Problem : luog

[TJOI2017] DNA

序 乓乓球 话说天津的玩梗真的是... 正文 做这个题,其实暴力一分也得不了....因为我的 \(O(nlogn)\) 不开 O2 最快的点也才跑了200+ms...然后大概就是这么想,我们在暴力的基础上优化一下.反正这样子应该可以简化反正就4个字符... 就是说,我们预处理出每个(子串)东西的 LCP,然后搞一搞吧.但是我知道的求 LCP 的快速方法只有后缀数组的 height,然后自然的联想到了 RMQ, 也就是说我们查询 suf(x), suf(y) 的 LCP 可以通过找一个 \(\mi

Hash总结

算法介绍 这个没什么好说的,就是一段比较简单的代码,具体的话要看题目. 自然溢出 这个是比较好用而且容易被卡的一种Hash方式. 用\(2^{64}\)当模数然后做\(Hash\),常数比较小. 单模数 也是比较好卡的\(Hash\)之一,算法就是把上面算法的\(2^{64}\)改成了\(Mod\),自己钦定的一个素数. 双模数 这个也是比较好卡的,希望某位dalao可以卡掉这个算法,让\(Hash\)不复存在. 考虑用两个模数,只有当Hash在这两个模数下都成立才是相等. 树哈希 考虑对于这个

【TJOI2017】DNA

题面 题解 对字符串一脸懵的我肯定只能用$FFT$这种暴力方法水过啊... 将后面那个字符串翻转一下,对$\text{AGCT}$分别统计,用$FFT$就可以啦 代码 #include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<algorithm> #define RG register const int maxn(200010); const dou

UVA - 1368 DNA Consensus String

DNA Consensus String Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description  Figure 1. DNA (Deoxyribonucleic Acid) is the molecule which contains the genetic instructions. It consists of four different nuc