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. Unet 项目部分代码学习

    github地址:https://github.com/orobix/retina-unet 主程序: ################################################ ...

  2. 饮冰三年-人工智能-Python-11之HelloWorld

    1:安装不在介绍,下载软件下一步即可,配置环境变量(Python37会默认配置环境变量的) D:\Programs\Python\Python37\Scripts\;D:\Programs\Pytho ...

  3. Just oj 2018 C语言程序设计竞赛(高级组)H: CBT?

    H: CBT? 时间限制: 1 s      内存限制: 128 MB      提交 我的状态 题目描述 对于二叉树,如果这棵树的节点排布是按行从上到下,每行从左到右挨个放置,中间不会有空闲的节点. ...

  4. StackService.Redis 应用

    如今StackService.Redis已经转向商业版本.4.0以下的低版本依然免费和开源. 吴双,Redis系列命令拾遗分享 http://www.cnblogs.com/tdws/tag/NoSq ...

  5. gradle编译命令 & 自动打包等

    ./gradlew -v 版本号,首次运行,没有gradle的要下载的哦. ./gradlew clean 删除HelloWord/app目录下的build文件夹 ./gradlew build 检查 ...

  6. Visual Studio 2015 插件开发入门

    (1)安装 Visual Studio 2015 的时候选择 Visual Studio 扩展性工具(Visual Studio Extensibility Tools).对于已经安装好 Visual ...

  7. [转] web前端js构造无法销毁的类UUID识别码,识别浏览器设备唯一性

    用户行为统计在如今的前端生态中已是稀松寻常,如各种站长统计工具.识别用户访问客户端唯一性是必要的实现,对于web前端获取的设备信息,一般容易想到的是通过navigator.userAgent,但相同设 ...

  8. javascript 语句和严格模式(三)

    一.语句 javascript程序由语句组成,语句遵守特定的语法规则. block break continue empty if...else switch try catch var functi ...

  9. 关于浏览器对html, js,css的解析先后顺序的理解

    1.首先要了解页面的结构(包含哪些元素?哪些计算机语言能够在页面中运行 ) (1)html          不仅可以包含文字,还可以包含图片.链接,甚至音乐.程序等非文字元素的标记语言       ...

  10. java运算符-逻辑、三元运算符

    1.逻辑运算符 逻辑运算符,它是用于布尔值进行运算的,运算的最终结果为布尔值true或false. 运算符 运算规则 范例 结果 & 与 false&true False | 或 fa ...