BZOJ


\(Description\)

给定两个串\(S,T\)以及一个数\(k\),求\(T\)中有多少个子串,满足和\(S\)的编辑距离不超过\(k\)。

\(|S|+|T|\leq10^5,\ k\leq 5\)。

\(Solution\)

考虑枚举\(T\)的每个后缀\(i\)(注意后缀是指啥= =),求后缀\(i\)中有哪些前缀满足条件。

怎么处理编辑距离呢?\(k\)很小,直接搜。

设\(S,T\)分别匹配到\(x,y\)位置,可以用\(SA\)求\(LCP(x,y)\),然后直接跳到下一个不匹配位置。

如果\(S_x\neq T_y\),那么有三种选择:删掉\(T_y\to x,y+1\),在\(T_y\)前插入一个\(S_x\to x+1,y\),把\(T_y\)替换成\(S_x\to x+1,y+1\)。

所以\(DFS\)的复杂度是\(3^k\)的。

匹配完\(S\)串后,如果还剩下一些可用编辑距离\(rest\),显然此时前缀\([y-rest,y+rest]\)都满足条件,差分一下即可。注意这些前缀不要算重(一个位置只能算一次)。

复杂度\(O(n\log n+n3^k)\)。


//10296kb	3476ms
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=1e5+7; int na,nb,Now,L,R,sum[N];
char s[N];
struct Suffix_Array
{
int sa[N],sa2[N],rk[N],tm[N],ht[N],Log[N],st[17][N];
inline int LCP(int l,int r)
{
l=rk[l], r=rk[r]; if(l>r) std::swap(l,r);
++l; int k=Log[r-l+1];
return std::min(st[k][l],st[k][r-(1<<k)+1]);
}
void Build(const char *s,const int n)
{
int m=27,*x=rk,*y=sa2;
for(int i=0; i<=m; ++i) tm[i]=0;
for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]-'A'+1];
for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
for(int i=n; i; --i) sa[tm[x[i]]--]=i;
for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
{
for(int i=n-k+1; i<=n; ++i) y[++p]=i;
for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k; for(int i=0; i<=m; ++i) tm[i]=0;
for(int i=1; i<=n; ++i) ++tm[x[i]];
for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i]; std::swap(x,y), x[sa[1]]=p=1;
for(int i=2; i<=n; ++i)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
if(p>=n) break;
}
for(int i=1; i<=n; ++i) rk[sa[i]]=i;
ht[1]=0;
for(int i=1,k=0; i<=n; ++i)
{
if(rk[i]==1) continue;
if(k) --k;
int p=sa[rk[i]-1];
while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;
ht[rk[i]]=k;
}
st[0][1]=ht[1];
for(int i=2; i<=n; ++i) Log[i]=Log[i>>1]+1, st[0][i]=ht[i];
for(int j=1; j<=Log[n]; ++j)
for(int t=1<<j-1,i=n-t; i; --i)
st[j][i]=std::min(st[j-1][i],st[j-1][i+t]);
}
}sa; inline void Upd(int l,int r)
{
l=std::max(l,Now), r=std::min(r,nb), L=std::min(l,L), R=std::max(r+1,R);
++sum[l], --sum[r+1];//注意可行前缀位置的限制(在Now~nb内)
}
void DFS(int x,int y,int rest)
{
int t=sa.LCP(x,y+na+1);
x+=t, y+=t;
if(x>na||y>nb)
{
int d=rest-(na-x+1);
if(d>=0) Upd(y-1-d,y-1+d);
return;
}
if(rest) --rest, DFS(x+1,y,rest), DFS(x,y+1,rest), DFS(x+1,y+1,rest);
} int main()
{
int K; scanf("%d%s",&K,s+1);
na=strlen(s+1), s[na+1]='[';
scanf("%s",s+na+2), nb=strlen(s+na+2);
const int n=na+nb+1; sa.Build(s,n);
int ans=0;
for(int i=1,delta=std::max(0,na-K); i+delta<=nb; ++i)
{
Now=i, L=N, R=0, DFS(1,i,K);
for(int j=L; j<=R; ++j) ans+=(sum[j]+=sum[j-1])>0;
for(int j=L; j<=R; ++j) sum[j]=0;
}
printf("%d\n",ans); return 0;
}

BZOJ.4340.[BJOI2015]隐身术(后缀数组 搜索)的更多相关文章

  1. BZOJ4340:[BJOI2015]隐身术(后缀数组,ST表,DFS)

    Description 给定两个串A,B.请问B中有多少个非空子串和A的编辑距离不超过K? 所谓“子串”,指的是B中连续的一段.不同位置的内容相同的子串算作多个. 两个串之间的“编辑距离”指的是把一个 ...

  2. [BZOJ4340][BJOI2015]隐身术(后缀数组)

    考虑到K很小,于是可以暴搜每次用的是哪种操作,跳过AB相等的字符可以用SA求LCP加速. 主要流程就是,枚举B的每个后缀,对每个后缀统计合法前缀个数.DFS搜索每次决策,用SA跳过相同字符,当A或B匹 ...

  3. 【BZOJ 3473】 字符串 (后缀数组+RMQ+二分 | 广义SAM)

    3473: 字符串 Description 给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串? Input 第一行两个整数n,k. 接下来n行每行一个字符串 ...

  4. BZOJ 3172([Tjoi2013]单词-后缀数组第一题+RMQ)

    3172: [Tjoi2013]单词 Time Limit: 10 Sec   Memory Limit: 512 MB Submit: 268   Solved: 145 [ Submit][ St ...

  5. BZOJ 2865 字符串识别 | 后缀数组 线段树

    集训讲字符串的时候我唯一想出正解的题-- 链接 BZOJ 2865 题面 给出一个长度为n (n <= 5e5) 的字符串,对于每一位,求包含该位的.最短的.在原串中只出现过一次的子串. 题解 ...

  6. BZOJ 3230 相似子串 | 后缀数组 二分 ST表

    BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过 ...

  7. BZOJ 4278: [ONTAK2015]Tasowanie 后缀数组 + 贪心 + 细节

    Code: #include <bits/stdc++.h> #define setIO(s) freopen(s".in", "r", stdin ...

  8. BZOJ 4278: [ONTAK2015]Tasowanie (后缀数组 / 二分+hash)

    直接归并,然后如果哪边的后缀字典序比较小就去哪边,然后就可以后缀数组 博客传送门- 但是本蒟蒻不会后缀数组 Upd:Upd:Upd:现在会了233.一道差不多的题:BZOJ 1692: [Usaco2 ...

  9. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

随机推荐

  1. 高斯消元模板!!!bzoj1013

    /* 高斯消元模板题 n维球体确定圆心必须要用到n+1个点 设圆心坐标(x1,x2,x3,x4...xn),半径为C 设第i个点坐标为(ai1,ai2,ai3,,,ain)那么对应的方程为 (x1-a ...

  2. jquery checkbox勾选/取消勾选只能操作一次的诡异问题

    第一次执行,没问题,但第二次执行就有问题了,选择不了 解决办法:把attr()换成prop() $("#CheckedAll").click(function () { if ($ ...

  3. 调试WebApi的一些方法

    1.Get方法时,直接用浏览器访问 2.Postman 3.用HttpClient调用 privatevoid GetData() { using (HttpClient client = new H ...

  4. Python推荐系统库--Surprise理论

    Surprise Surprise是scikit系列中的一个.Surprise的User Guide有详细的解释和说明 支持多种推荐算法 基础算法/baseline algorithms 基于近邻方法 ...

  5. 一脸懵逼学习KafKa集群的安装搭建--(一种高吞吐量的分布式发布订阅消息系统)

    kafka的前言知识: :Kafka是什么? 在流式计算中,Kafka一般用来缓存数据,Storm通过消费Kafka的数据进行计算.kafka是一个生产-消费模型. Producer:生产者,只负责数 ...

  6. C#学习-const和readonly

    const是表示为常量的关键字,一旦赋值就不能改变了.是程序编译时候CLR就将const的值编译到IL代码中了. readonly也是常量的关键的字: 所以,有了这两个关键字的比较.readonly肯 ...

  7. IDEA上创建 Maven SpringBoot+mybatisplus+thymeleaf 项目

    概述 在WEB领域,Java也是在不断的探索和改进,从开始的JSP--->Struts1--->Struts2+Spring--->Spring MVC--->SpringBo ...

  8. MySQl 查询性能优化相关

    0. 1.参考 提升网站访问速度的 SQL 查询优化技巧 缓存一切数据,读取内存而不是硬盘IO 如果你的服务器默认情况下没有使用MySQL查询缓存,那么你应该开启缓存.开启缓存意味着MySQL 会把所 ...

  9. python多线程之t.setDaemon(True) 和 t.join()

    0.目录 1.参考2.结论    (1)通过 t.setDaemon(True) 将子线程设置为守护进程(默认False),主线程代码执行完毕后,python程序退出,无需理会守护子线程的状态.    ...

  10. 【转】利用 selenium 的 webdrive 驱动 headless chrome

    1.参考 使用 headless chrome进行测试 2.概念 Headless模式解决了什么问题: 自动化工具例如 selenium 利用有头浏览器进行测试,面临效率和稳定性的影响,所以出现了 H ...