传送门


两种做法:

①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. (网页)Java日志记录框架Logback配置详解(企业级应用解决方案)(转)

    转自CSDN: 前言 Logback是现在比较流行的一个日志记录框架,它的配置比较简单学习成本相对较低,所以刚刚接触该框架的朋友不要畏惧,多花点耐心很快就能灵活应用了.本篇博文不会具体介绍Logbac ...

  2. LeetCode题解之Insert into a Binary Search Tree

    1.题目描述 2.分析 插入算法. 3.代码 TreeNode* insertIntoBST(TreeNode* root, int val) { insert(root, val); return ...

  3. AOP缓存实现

    输入参数索引作为缓存键的实现 using MJD.Framework.CrossCutting; using MJD.Framework.ICache; using System; using Sys ...

  4. vue-cli快速原型开发

    我们知道vue-cli提供了一套如何快速搭建vue开发脚手架的工具,虽然好用,但是有的时候我们还是嫌麻烦,因为就想快速开发调试一个组件,这时我们就可以使用vue-cli 3.x以上版本的一个好特性: ...

  5. CSS| 框模型-margin

    CSS margin 属性 设置外边距的最简单的方法就是使用 margin 属性. margin 属性接受任何长度单位,可以是像素.英寸.毫米或 em. margin 可以设置为 auto.更常见的做 ...

  6. 面向对象的封装与隐藏 this

    当我们创建一个对象的时候,我们可以通过‘对象.属性’的方式,对对象的属性进行赋值. 这里赋值操作要受到属性的数据类型和存储范围的制约,但是除此之外,没有其他制约条件. 但是实际问题中我们需要给这个属性 ...

  7. rbac models

    class Permission(models.Model): """ 权限表 """ perm_name = models.CharFie ...

  8. 【PAT】B1069 微博转发抽奖(20 分)

    一开始并没有做出来,关键是没有认真理解题,每次做题之前都应该认真读题,自己把样例模拟一下,防止漏掉信息,减慢自己写代码的速度 此题的重点在于规划逻辑,以及如何储存中奖者,用map最好,否则查找并不方便 ...

  9. nginx基础知识总结

    1.nginx的工作模式 master/worker工作模式: 一个master进程: 负载加载和分析配置文件.管理worker进程.平滑重启升级等. 一个或多个worker进程 处理并响应用户请求 ...

  10. Linux 小知识翻译 - 「架构 续」(arch)

    上次,从「计算机的内部构造」的角度解释了架构这个术语.这次,介绍下架构中经常提到的「i386架构」及之后的「i486」,「i586」. 安装Linux的时候,很多人即使不了解但也会经常听到i386架构 ...