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. Workbench热水泵系统

    1.创建一个新的项目,首先新建一个station,打开platform(链接配置,端口设置,账户设置) 新建文件夹之后模板的选取从palette中选择. 第一步新建station,键入station名 ...

  2. django----Form实时更新两种方式

    在ModelForm需要知道: from app03 import models from django.forms import ModelForm class UserForm(ModelForm ...

  3. hdu2871 区间合并(类似poj3667)+vector应用

    用vector进行插入和删除操作! 总是有些地方处理不好,对拍了才知道错在哪里,, /* 给定一些操作 reset 清空 new a ,申请最左边的连续a个空间 free a,清空a所在的块 get ...

  4. spfa+01 规划

    尼玛的哪里错了.. /* 在有向图上找一个环,使结点权值和/边权和的比例值最大 01规划,设比例为l,那么将每条边的权值改成a[u]-l*w,如果有正权环,则比例l可行 如何判图中存在正权环?将 权值 ...

  5. bzoj 4816

    这题是莫比乌斯反演的典型题也是很有趣的题. 题意:求,其中f为为斐波那契数列 那么首先观察一下指数,发现是我们熟悉的形式,可以转化成这样的形式: 令T=kd,且假设n<m,有: 令 则原式= 这 ...

  6. python接口自动化测试三十三:获取时间戳(10位和13位)

    很多时候,在调用接口时,需要对请求进行签名.需要用到unix时间戳. 在python里,在网上介绍的很多方法,得到的时间戳是10位.而java里默认是13位(milliseconds,毫秒级的). 下 ...

  7. Nginx详解二十:Nginx深度学习篇之HTTPS的原理和作用、配置及优化

    一.HTTPS原理和作用: 1.为什么需要HTTPS?原因:HTTP不安全1.传输数据被中间人盗用.信息泄露2.数据内容劫持.篡改 2.HTTPS协议的实现对传输内容进行加密以及身份验证 对称加密:加 ...

  8. Java+selenium之WebDriver页面元素的操作(三)

    1. 输入框(text field or textarea) WebElement we = driver.findElement(By.id("id")); //将输入框清空 w ...

  9. cmake方式使用vlfeat

    目录 environment statement compile vlfeat with cmake compile example project with cmake 1. make sure c ...

  10. 【C++ Primer | 10】泛型算法

    #include<iostream> #include<algorithm> #include<vector> #include<string> #in ...