[洛谷P3763] [TJOI2017]DNA

洛谷题目链接:[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;
}
posted @ 2019-01-24 14:51  Brave_Cattle  阅读(185)  评论(0编辑  收藏  举报