传送门


两种做法:

①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. 警告!中国90%AI初创企业将在两年内落败出局

    https://mp.weixin.qq.com/s/-RkyLda1jovaHBlBTsi-BA 近年来,中国涌现了一大批AI初创企业,但AI热潮也伴随着泡沫.由于近期市场资金紧缩,投资者发出警告, ...

  2. NFS共享权限挂载

    mount -t nfs 192.168.2.203:/data/lys /lys -o proto=tcp -o nolock mount 172.16.2.18:/home/arcgisserve ...

  3. loadrunner 脚本开发-文件读写操作

    脚本开发-文件读写操作 by:授客 QQ:1033553122 函数说明 函数原型: size_t fwrite( const void *buffer, size_t size, size_t co ...

  4. Spark内存分配诊断

    1.JVM自带众多内存诊断的工具,例如:JMap,JConsole等,第三方IBM JVM Profile Tools等. 2.日志!在开发.测试.生产环境中最合适的就是日志,特别是Driver产生的 ...

  5. Websocket通信过程

    1. 客户端与服务器建立连接 2. 客户端通过session向服务器发送消息 3. 服务器接收客户端的消息,调用服务器端的onMessage()方法包装.生成消息内容(新的消息包括客户端ID) 4. ...

  6. sql最简单的查询语句

    -- 2 **************************************************** -- 最简单的查询语句 -- 2.1 ----------------------- ...

  7. 怎样让引用类库的类在HelpPage上显示Description

        最近在做 web api 开发的时候遇到这样的问题,即 HelpPage 里只能显示 api 控制器上的注释,对于那些引用了外部类库的类(比如POST提交需要用到的类),就无法显示它们的备注, ...

  8. 峰值QPS/QPS/PV/UV/服务器数量/并发数/吐吞量/响应时间计算公式

    QPS:每秒查询率(Query Per Second) ,每秒的响应请求数,也即是最大吞吐能力.QPS = req/sec = 请求数/秒QPS统计方式 [一般使用 http_load 进行统计]QP ...

  9. 团队项目管理:Github项目误删恢复记录

    参考: 准备更换git托管,如何迁移原git仓库 Github项目误删恢复记录 今天正常上线打卡,发现组织的线上Github仓库被误删了..本来是一场悲剧,所幸在本地的垃圾箱中翻出了还没有删除的本地仓 ...

  10. SQL Server2008 18456错误

    1.以windows验证模式进入数据库管理器.   第二步:右击sa,选择属性:   在常规选项卡中,重新填写密码和确认密码(改成个好记的).把强制实施密码策略去掉.   第三步:点击状态选项卡:勾选 ...