传送门


两种做法:

①SA

将两个串拼在一次建立后缀数组,把\(height\)数组求出来,然后对于\(S\)中每一个长度为\(T\)的串和\(T\)暴力匹配,每一次找到最长的\(LCP\)匹配,如果失配次数\(>3\)就直接退出。总复杂度\(O(T(NlogN+4N))\)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
//This code is written by Itst
using namespace std;

const int MAXN = 2e5 + 7;
char s[MAXN];
int sa[MAXN] , rk[MAXN << 1] , tp[MAXN << 1] , pot[MAXN] , h[MAXN] , ST[19][MAXN];
int ls , L , maxN;

void sort(int p){
    memset(pot , 0 , sizeof(int) * (maxN + 1));
    for(int i = 1 ; i <= L ; ++i)
        ++pot[rk[i]];
    for(int i = 1 ; i <= maxN ; ++i)
        pot[i] += pot[i - 1];
    for(int i = 1 ; i <= L ; ++i)
        sa[++pot[rk[tp[i]] - 1]] = tp[i];
    memcpy(tp , rk , sizeof(int) * (L + 1));
    for(int i = 1 ; i <= L ; ++i)
        rk[sa[i]] = rk[sa[i - 1]] + (tp[sa[i]] != tp[sa[i - 1]] || tp[sa[i] + p] != tp[sa[i - 1] + p]);
    maxN = rk[sa[L]];
}

void init(){
    maxN = 26;
    for(int i = 1 ; i <= L ; ++i)
        rk[tp[i] = i] = s[i] - 'A' + 1;
    sort(0);
    for(int i = 1 ; maxN != L ; i <<= 1){
        int cnt = 0;
        for(int j = 1 ; j <= i ; ++j)
            tp[++cnt] = L - i + j;
        for(int j = 1 ; j <= L ; ++j)
            if(sa[j] > i)
                tp[++cnt] = sa[j] - i;
        sort(i);
    }
    for(int i = 1 ; i <= L ; ++i){
        if(rk[i] == 1)
            continue;
        int t = rk[i];
        h[t] = max(0 , h[rk[i - 1]] - 1);
        while(s[sa[t] + h[t]] == s[sa[t - 1] + h[t]])
            ++h[t];
    }
}

void init_ST(){
    for(int i = 2 ; i <= L ; ++i)
        ST[0][i] = h[i];
    for(int i = 1 ; (1 << i) + 1 <= L ; ++i)
        for(int j = 2 ; j + (1 << i) - 1 <= L ; ++j)
            ST[i][j] = min(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]);
}

inline int qST(int x , int y){
    if(x > y)
        swap(x , y);
    int t = log2(y - x);
    return min(ST[t][x + 1] , ST[t][y - (1 << t) + 1]);
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    //freopen("out","w",stdout);
#endif
    int T;
    for(scanf("%d" , &T) ; T ; --T){
        scanf("%s" , s + 1);
        ls = strlen(s + 1);
        scanf("%s" , s + ls + 1);
        L = strlen(s + 1);
        init();
        init_ST();
        int ans = 0;
        for(int i = 1 ; i <= ls - (L - ls) + 1 ; ++i){
            int posS = i , posT = ls + 1 , cnt = 0;
            while(cnt <= 3 && posT <= L){
                int t = qST(rk[posS] , rk[posT]);
                posT += t;
                posS += t;
                if(posT > L)
                    break;
                ++cnt;
                ++posS;
                ++posT;
            }
            if(cnt <= 3)
                ++ans;
        }
        cout << ans << endl;
    }
    return 0;
}

②NTT

将模板串翻转,对于\(AGCT\)每一个做一次\(NTT\):如果匹配串第\(i\)位为当前字符则\(a_i=1\)否则\(a_i = 0\),模板串同理。然后NTT得到两个数组的卷积,就可得到匹配串每个位置的子串与模板串之间匹配字符为\(A\)的匹配次数。复杂度\(O(4TNlogN)\)

#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
//This code is written by Itst
using namespace std;

const int G = 3 , MOD = 998244353 , INV = 332748118 , MAXN = (1 << 18) + 7;
const char exp[] = "AGCT";
int num[MAXN] , dir[MAXN] , sum[MAXN] , A[MAXN] , B[MAXN];
int need , inv_need , lS , lT;
char s[MAXN] , t[MAXN];

inline int poww(long long a , int b){
    int times = 1;
    while(b){
        if(b & 1)
            times = times * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return times;
}

void init(int x){
    need = 1;
    while(need < x)
        need <<= 1;
    inv_need = poww(need , MOD - 2);
    for(int i = 1 ; i <= need ; ++i)
        dir[i] = (dir[i >> 1] >> 1) | (i & 1 ? need >> 1 : 0);
}

void NTT(int *arr , int tp){
    for(int i = 1 ; i < need ; ++i)
        if(i < dir[i])
            arr[i] ^= arr[dir[i]] ^= arr[i] ^= arr[dir[i]];
    for(int i = 1 ; i < need ; i <<= 1){
        int wn = poww(tp == 1 ? G : INV , (MOD - 1) / i / 2);
        for(int j = 0 ; j < need ; j += i << 1){
            long long w = 1;
            for(int k = 0 ; k < i ; ++k , w = w * wn % MOD){
                int x = arr[j + k] , y = arr[i + j + k] * w % MOD;
                arr[j + k] = x + y >= MOD ? x + y - MOD : x + y;
                arr[i + j + k] = x < y ? x - y + MOD : x - y;
            }
        }
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    //freopen("out","w",stdout);
#endif
    int T;
    for(scanf("%d" , &T) ; T ; --T){
        scanf("%s %s" , s + 1 , t + 1);
        lS = strlen(s + 1);
        lT = strlen(t + 1);
        init(lS + lT);
        memset(sum , 0 , sizeof(int) * need);
        reverse(t + 1 , t + lT + 1);
        for(int j = 0 ; j < 4 ; ++j){
            memset(A , 0 , sizeof(int) * need);
            memset(B , 0 , sizeof(int) * need);
            char c = exp[j];
            for(int i = 1 ; i <= lS ; ++i)
                A[i] = s[i] == c;
            for(int i = 1 ; i <= lT ; ++i)
                B[i] = t[i] == c;
            NTT(A , 1); NTT(B , 1);
            for(int i = 0 ; i < need ; ++i)
                A[i] = 1ll * A[i] * B[i] % MOD;
            NTT(A , -1);
            for(int i = lT + 1 ; i <= lS + 1 ; ++i)
                sum[i] = sum[i] + A[i] >= MOD ? sum[i] + A[i] - MOD : sum[i] + A[i];
        }
        int cnt = 0;
        for(int i = lT + 1 ; i <= lS + 1 ; ++i)
            cnt += 1ll * sum[i] * inv_need % MOD >= lT - 3;
        cout << cnt << endl;
    }
    return 0;
}

Luogu3763 TJOI2017 DNA NTT/SA的更多相关文章

  1. [洛谷P3763] [TJOI2017]DNA

    洛谷题目链接:[TJOI2017]DNA 题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其 ...

  2. [TJOI2017] DNA - 后缀数组,稀疏表

    [TJOI2017] DNA Description 求模式串与主串的匹配次数,容错不超过三个字符. Solution 枚举每个开始位置,进行暴力匹配,直到失配次数用光或者匹配成功.考虑到容错量很小, ...

  3. bzoj4892 [TJOI2017]DNA

    bzoj4892 [TJOI2017]DNA 给定一个匹配串和一个模式串,求模式串有多少个连续子串能够修改不超过 \(3\) 个字符变成匹配串 \(len\leq10^5\) hash 枚举子串左端点 ...

  4. [TJOI2017]DNA --- 后缀数组

    [TJOI2017]DNA 题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S, 有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个 ...

  5. BZOJ.4892.[TJOI2017]DNA(后缀自动机/后缀数组)

    题目链接 \(Description\) 给出两个串\(S,T\),求\(T\)在\(S\)中出现了多少次.出现是指.可以有\(3\)次(\(3\)个字符)不匹配(修改使其匹配). \(Solutio ...

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

    题目描述 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表现出吃藕的性状 ...

  7. 洛谷3763:[TJOI2017]DNA——题解

    https://www.luogu.org/problemnew/show/P3763 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是 ...

  8. BZOJ4892:[TJOI2017]dna(hash)

    Description 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够表 ...

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

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

随机推荐

  1. for each....in、for in、for of

    一.一般的遍历数组的方法: var array = [1,2,3,4,5,6,7]; for (var i = 0; i < array.length; i) { console.log(i,a ...

  2. 传统BI还是自助式BI---BI与数据分析 ZT

    自助式BI或者自助式数据分析是最近几年兴起的一个概念.根据Gartner发布的信息,Self Service Business Intelligence(SSBI)被定义为“终端用户在被批准和支持的平 ...

  3. Oracle 11g数据库的创建

    由于是自己自学Oracle,如果有问题,请大家指出,谢谢! Oracle提供了DBCA来创建数据库,对于初学者来说使用DBCA创建数据库简化了很多工作和设置,直接在交互界面即可实现所有的功能. 然而对 ...

  4. loadrunner 脚本开发-字符串编码转换

    字符串编码转换 by:授客 QQ:1033553122   相关函数 lr_convert_string_encoding函数 功能:字符串编码转换 原型: int lr_convert_string ...

  5. Flutter 布局(八)- Stack、IndexedStack、GridView详解

    本文主要介绍Flutter布局中的Stack.IndexedStack.GridView控件,详细介绍了其布局行为以及使用场景,并对源码进行了分析. 1. Stack A widget that po ...

  6. C语言程序试题

    一个无向连通图G点上的哈密尔顿(Hamiltion)回路是指从图G上的某个顶点出发,经过图上所有其他顶点一次且仅一次,最后回到该顶点的路劲.一种求解无向图上哈密尔顿回路算法的基础实现如下: 假设图G存 ...

  7. Linux源码解析-内核栈与thread_info结构详解

    1.什么是进程的内核栈? 在内核态(比如应用进程执行系统调用)时,进程运行需要自己的堆栈信息(不是原用户空间中的栈),而是使用内核空间中的栈,这个栈就是进程的内核栈 2.进程的内核栈在计算机中是如何描 ...

  8. fedora 28 安装 wine 运行 uTorrent 解决linux 端,pt 资源下载问题

    fedora 28 仓库中,资源比较多.使用 wine 运行windows 程序,可以一定程度上解决软件跨平台问题. 搜索: Last metadata expiration check: :: ag ...

  9. mysql启动失败又一例

    搭的wordpress报错: 后台用的mysql,之前也崩过,原因是虚拟内存耗尽,通过增加swap空间最终让数据重新启动. 但仅过一晚上,数据库再次崩溃.看来要查一查是什么程序耗尽资源. 执行top, ...

  10. Linux防火墙基础与编写防火墙规则

    Iptables采用了表和链的分层结构,每个规则表相当于内核空间的一个容器,根据规则集的不同用途划分为默认的四个表,raw表,mangle表,nat表,filter表,每个表容器内包括不同的规则链,根 ...