• [洛谷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,kin[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;
    }
    
  • 相关阅读:
    consumer详解
    消费幂等
    死信队列
    消息重试
    负载均衡
    存储层
    producer消息
    消息发送与接收
    TCC
    form表单提交前进行加密
  • 原文地址:https://www.cnblogs.com/BCOI/p/10314462.html
Copyright © 2020-2023  润新知