传送门


两种做法:

①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. 虚拟 DOM

    虚拟DOM :virtual dom(以下简称vdom,是vue和react的核心),使用比较简单. 一,vdom是什么,为何会存在vdom 1,什么是vdom:用js模拟DOM结构,DOM操作非常‘ ...

  2. 读懂SAP Leonardo物联网平台

    读懂SAP Leonardo物联网平台 https://blog.csdn.net/weixin_42137700/article/details/81903290 本文比较系统.全面地介绍了SAP ...

  3. WebLogic 11g的安装与配置详谈配置详谈

     之前以weblogic8.1为例介绍了其具体安装,但是由于现在weblogic 11g毕竟使用越来越广泛 ,因此,下面将介绍weblogic 11g的具体安装以及配置: 一.安装步骤(基本跟之前we ...

  4. css文本属性用法总结

    稍稍总结了下css文本的一些属性用法,自己忘记的时候也可以用来查查,不用去查网站那么麻烦. 下面是部分总结,也希望对其他人有用 文本修饰 (1)text-decoration:  文本修饰(横线) 4 ...

  5. 四元数与欧拉角(RPY角)的相互转换

    RPY角与Z-Y-X欧拉角 描述坐标系{B}相对于参考坐标系{A}的姿态有两种方式.第一种是绕固定(参考)坐标轴旋转:假设开始两个坐标系重合,先将{B}绕{A}的X轴旋转$\gamma$,然后绕{A} ...

  6. 查看neighbors大小对K近邻分类算法预测准确度和泛化能力的影响

    代码: # -*- coding: utf-8 -*- """ Created on Thu Jul 12 09:36:49 2018 @author: zhen &qu ...

  7. [20171211]ora-16014 11g.txt

    [20171211]ora-16014 11g.txt --//上午测试了10g下备库log_archive_dest_1参数配置VALID_FOR=(ONLINE_LOGFILES,ALL_ROLE ...

  8. Hibernate 中的 idclass mapping 问题

    关于出现 idclass mapping 运行错误 @IdClass 注释通常用于定义包含复合键id的Class.即多个属性的关键复合. @IdClass(CountrylanguageEntityP ...

  9. Django框架的使用教程--站点的管理[七]

    Django的站点管理 创建超级管理员命令(密码要8位) python manage.py createsuperuser 进入站点管理 注册模型类 from django.contrib impor ...

  10. linux 下正则匹配时间命名格式的文件夹

    用正则表达式匹配时间格式命名的文件夹 ls mypath | grep -E "[0-9]{4}-[0-9]{1,2}" mypath为需要查询的目录 查询出来的文件夹格式为:例 ...