洛谷题目链接:[TJOI2017]DNA

题目描述

加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状。现在研究人员想知道这个基因在DNA链S0上的位置。所以你需要统计在一个表现出吃藕性状的人的DNA序列S0上,有多少个连续子串可能是该基因,即有多少个S0的连续子串修改小于等于三个字母能够变成S。

输入输出格式

输入格式:

第一行有一个数T,表示有几组数据 每组数据第一行一个长度不超过10^5的碱基序列S0

每组数据第二行一个长度不超过10^5的吃藕基因序列S

输出格式:

共T行,第i行表示第i组数据中,在S0中有多少个与S等长的连续子串可能是表现吃藕性状的碱基序列

输入输出样例

输入样例#1:

1

ATCGCCCTA

CTTCA

输出样例#1:

2

说明

对于20%的数据,S0,S的长度不超过10^4

对于100%的数据,S0,S的长度不超过10^5,0<T<=10

题意: 问字符串\(s\)中有多少个长度为\(n0\)的连续的子串与字符串\(s0\)的不同在\(3\)个以内.


题解: 考虑在\(s\)中枚举字符串的起点,如果当前枚举到的位置与\(s0\)中的对应位置相同的话,就向后延伸它们的\(lcp\)的长度.如果不相同,就计数器加\(1\),继续向后枚举,如果枚举长度超过\(s0\)的长度\(n0\)了,说明这次枚举的起点合法.

求\(lcp\)的过程可以用哈希预处理二分长度的方法在\(O(logn)\)的时间内求出,也可以后缀数组预处理在\(O(1)\)的时间内求出.

用后缀数组预处理主要是用到了这个性质:\(lcp(suffix(sa[i]),suffix(sa[j]))=min\{height[k]\}(i<j,k\in[i,j])\)

这样就可以在求出\(height\)数组的情况下用倍增预处理出一段连续排名的\(height\)数组的最小值,然后\(O(1)\)查询了.

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 2e5+5; int T, n, n0, m, sa[N], rk[N], sec[N], buk[N], height[N], f[25][N], Log[N], ans = 0;
char s[N], s0[N]; void rsort(){
for(int i = 0; i <= m; i++) buk[i] = 0;
for(int i = 1; i <= n; i++) buk[rk[i]]++;
for(int i = 1; i <= m; i++) buk[i] += buk[i-1];
for(int i = n; i >= 1; i--) sa[buk[rk[sec[i]]]--] = sec[i];
} void SuffixArray(){
for(int i = 1; i <= n; i++) rk[i] = s[i], sec[i] = i;
m = 260; rsort(); int num = 0;
for(int l = 1; l <= n && num < n; l <<= 1){
num = 0;
for(int i = 1; i <= l; i++) sec[++num] = n-l+i;
for(int i = 1; i <= n; i++) if(sa[i] > l) sec[++num] = sa[i]-l;
rsort(); swap(rk, sec), rk[sa[1]] = num = 1;
for(int i = 2; i <= n; i++)
rk[sa[i]] = (sec[sa[i]] == sec[sa[i-1]] && sec[sa[i]+l] == sec[sa[i-1]+l]) ? num : ++num;
m = num;
}
} void get_height(){
int j, k = 0;
for(int i = 1; i <= n; i++){
if(k) k--;
j = sa[rk[i]-1];
while(s[i+k] == s[j+k]) k++;
height[rk[i]] = k;
}
} void init(){
for(int i = 1; i <= n; i++) f[0][i] = height[i];
for(int j = 1; j <= 20; j++)
for(int i = 1; i+(1<<j)-1 <= n+n0; i++)
f[j][i] = min(f[j-1][i], f[j-1][i+(1<<(j-1))]);
} int lcp(int x, int y){
x = rk[x], y = rk[y]; if(x > y) swap(x, y);
x++;int lg = Log[y-x+1];
return min(f[lg][x], f[lg][y-(1<<lg)+1]);
} void clear(){
memset(f, 0x3f, sizeof(f));
ans = 0;
} int main(){
cin >> T; Log[0] = -1;
for(int i = 1; i <= 200000; i++) Log[i] = Log[i>>1]+1;
while(T--){
cin >> (s+1) >> (s0+1), n = strlen(s+1), n0 = strlen(s0+1);
for(int i = 1; i <= n0; i++) s[i+n] = s0[i]; n += n0;
SuffixArray(), get_height(), init();
for(int i = 1; i <= n-n0*2+1; i++){
int cnt = 0;
for(int j = 1; j <= n0 && cnt <= 3; j++){
if(s[i+j-1] != s[n-n0+j]) cnt++;
else j += lcp(i+j-1, n-n0+j)-1;
}
if(cnt <= 3) ans++;
}
cout << ans << endl;
clear();
}
return 0;
}

[洛谷P3763] [TJOI2017]DNA的更多相关文章

  1. 洛谷P3763 [Tjoi2017]DNA 【后缀数组】

    题目链接 洛谷P3763 题解 后缀数组裸题 在BZOJ被卡常到哭QAQ #include<algorithm> #include<iostream> #include< ...

  2. 洛谷P3763 [TJOI2017]DNA(后缀数组 RMQ)

    题意 题目链接 Sol 这题打死我也不会想到后缀数组的,应该会全程想AC自动机之类的吧 但知道这题能用后缀数组做之后应该就不是那么难了 首先把\(S\)和\(S0\)拼到一起跑,求出Height数组 ...

  3. 洛谷P3763 [TJOI2017]DNA(后缀自动机)

    传送门 好像用SAM写的很少诶…… 其实我一开始也没想到要用SAM的……主要是没有想到找的时候可以dfs…… 首先建一个SAM,然后跑一遍dfs,枚举一下下一位,如果相同直接继续,否则就花费一次次数来 ...

  4. [洛谷P3761] [TJOI2017]城市

    洛谷题目链接:[TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速 ...

  5. 洛谷P3759 [TJOI2017]不勤劳的图书管理员 【树状数组套主席树】

    题目链接 洛谷P3759 题解 树状数组套主席树板题 #include<algorithm> #include<iostream> #include<cstring> ...

  6. P3763 [TJOI2017]DNA

    链接:https://www.luogu.org/problemnew/show/P3763 题解: 挺水的一题后缀数组 枚举每一个开头用后缀数组判断能否在3次内匹配完

  7. 洛谷P3760 - [TJOI2017]异或和

    Portal Description 给出一个\(n(n\leq10^5)\)的序列\(\{a_n\}(\Sigma a_i\leq10^6)\),求该数列所有连续和的异或和. Solution 线段 ...

  8. 洛谷P3759 - [TJOI2017]不勤劳的图书管理员

    Portal Description 给出一个\(1..n(n\leq5\times10^4)\)的排列\(\{a_n\}\)和数列\(\{w_n\}(w_i\leq10^5)\),进行\(m(m\l ...

  9. 洛谷P3758 - [TJOI2017]可乐

    Portal Description 给出一张\(n(n\leq30)\)个点\(m(m\leq100)\)条边的无向图.初始时有一个可乐机器人在点\(1\),这个机器人每秒会做出以下三种行为之一:原 ...

随机推荐

  1. [leetcode-663-Equal Tree Partition]

    Given a binary tree with n nodes, your task is to check if it's possible to partition the tree to tw ...

  2. addeventlistener和attachevent

    区别: 1.ie8及以下版本前者无效,只能使用后者: 2,关于第三个参数,如果是true则捕获状态触发,为false;则为冒泡状态触发 何为冒泡,何为捕获? 这就好比捕鱼,冒泡吗,鱼向上吐泡泡,所以当 ...

  3. Python练习—文件

    1.随机生成20个两位正整数,将其升序排序后再写入文本文件data_asc.txt中! import random alist = [random.randint(10,100) for i in r ...

  4. Thunder团队第六周 - Scrum会议1

    Scrum会议1 小组名称:Thunder 项目名称:i阅app Scrum Master:王航 工作照片: 参会成员: 王航(Master):http://www.cnblogs.com/wangh ...

  5. Java学习个人备忘录之多线程

    进程:正在进行中的程序(直译). 线程:就是进程中一个负责程序执行的控制单元(执行路径) 一个进程中可以有多个执行路径,称之为多线程. 一个进程中至少要有一个线程. 开启多个线程是为了同时运行多部分代 ...

  6. shiro控制登陆成功后跳回之前的页面

    登陆之后跳回之前的页面是在做登陆注册模块时遇到的一个需求,也是很有必要的.若用户直接访问登陆页面,那可以控制它直接到首页,但是要用户没有登陆直接访问自己的购物车等需要经过身份认证的页面,或者因为ses ...

  7. 软工网络15团队作业4——Alpha阶段敏捷冲刺-4

    一.当天站立式会议照片: 二.项目进展 昨天已完成的工作: 完成程序副界面的设计与信息的输入统计 明天计划完成的工作: 日期等细致信息的处理 工作中遇到的困难: 对微信小程序开发的代码构成有了一些了解 ...

  8. centos7 安装 httpd并打开测试页

    systemctl start firewalld.service#启动firewallsystemctl stop firewalld.service#停止firewallsystemctl dis ...

  9. windows默认TEMP环境

    留着是为了等出问题的时候能找着改回来 Administrator 的用户变量 TEMP     %USERPROFILE%\AppData\Local\Temp TMP        %USERPRO ...

  10. Redis 学习之数据类型

    该文使用centos6.5 64位 redis-3.2.8 [root@localhost bin]# netstat -tunpl |grep 6379  查看redis 是否启动成功 一.Stri ...