题意

给出两个长度小于等于2000的小写字母串,四个问题:

  • A的最短子串不是B的子串
  • A的最短子串不是B的子序列
  • A的最短子序列不是B的子串
  • A的最短子序列不是B的子序列

分析

虽然求的是不公共,但是这还是一个字符串的匹配问题,只不过是求匹配不到。

对于子串的匹配问题,可以使用后缀自动机。然而对于子序列的匹配问题,提出一种新的数据结构,称为子序列自动机

子序列自动机是一个能够跑出一个串的所有子序列的有限状态机,基本思路是每个点存每一种字符下一次出现位置的点,这样就可以保证跑出所有的子序列,并且状态数是\(O(n)\)的。子序列自动机的构建方法很简单,只要从后往前扫一次,维护每个字符最后出现的位置即可,所以它只支持在前端添加字符。

这样的子序列自动机的空间复杂度都是\(O(n|s|)\),其中\(s\)为字符集。构建的时间复杂度为\(O(n|s|)\),一次转移是\(O(1)\)的。

然而如果字符集过大,这样的复杂度很明显是不能被支持的。注意到这其实是一个从后往前扫的过程,不断地维护一个序列,所以我们可以把这个数组换成一个可持久化线段树,那么这样的空间复杂度,构建复杂的都是\(O(nlog|s|)\),一次转移就变成了\(O(log|s|)\)。

那么前两问就直接枚举A串的左端点,直接在两种自动机上跑一下即可。

后两问是与子序列有关的,所以可以考虑动态规划。设\(f[i]\)表示在自动机上走到\(i\)号点至少是多长的子序列,那么每次扫描每个点,往后转移一下即可。最终答案为\(f[null]\)(或程序中的\(f[0]\),即空状态)。

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define print(x) printf("%d\n",x==inf?-1:x)
using namespace std;
int read(int a[]) {
char c=getchar();
int len=0;
for (;!isalpha(c);c=getchar());
for (;isalpha(c);c=getchar()) a[++len]=c-'a'+1;
return len;
}
const int maxn=2e3+10;
const int maxc=27;
const int inf=0x3f3f3f3f;
int a[maxn],b[maxn];
int n,m,f[maxn][maxn<<1];
struct SAM {
int t[maxn<<1][maxc],len[maxn<<1],link[maxn<<1],tot,last;
SAM ():tot(1),last(1) {}
void add(int x) {
int nw=++tot,i;
len[nw]=len[last]+1;
for (i=last;i && !t[i][x];i=link[i]) t[i][x]=nw;
if (i) {
int p=t[i][x];
if (len[p]==len[i]+1) link[nw]=p; else {
int q=++tot;
len[q]=len[i]+1;
for (int j=i;j && t[j][x]==p;j=link[j]) t[j][x]=q;
link[q]=link[p];
link[p]=link[nw]=q;
memcpy(t[q],t[p],sizeof t[p]);
}
} else link[nw]=1;
last=nw;
}
void build(int a[],int n) {
for (int i=1;i<=n;++i) add(a[i]);
}
int run(int a[],int n) {
int now=1;
for (int i=1;i<=n;++i) if (t[now][a[i]]) now=t[now][a[i]]; else return i;
return inf;
}
int size() {
return tot;
}
} sam;
struct LAM {
int t[maxn][maxc],aux[maxc],tot;
void build(int a[],int n) {
tot=n+1;
for (int i=n;i;--i) {
memcpy(t[i+1],aux,sizeof aux);
aux[a[i]]=i+1;
}
memcpy(t[1],aux,sizeof aux);
}
int run(int a[],int n) {
int now=1;
for (int i=1;i<=n;++i) if (t[now][a[i]]) now=t[now][a[i]]; else {
return i;
}
return inf;
}
int size() {
return tot;
}
} lam;
void up(int &x,int y) {
x=min(x,y);
}
void one() {
int ans=inf;
for (int i=1;i<=n;++i)
up(ans,sam.run(a+i-1,n-i+1));
print(ans);
}
void two() {
int ans=inf;
for (int i=1;i<=n;++i)
up(ans,lam.run(a+i-1,n-i+1));
print(ans);
}
void three() {
int ans=inf,dian=sam.size();
memset(f,0x3f,sizeof f);
f[0][1]=0;
for (int i=1;i<=n;++i) {
f[i][1]=0;
for (int j=1;j<=dian;++j) f[i][j]=f[i-1][j];
for (int j=1;j<=dian;++j) {
up(f[i][sam.t[j][a[i]]],min(f[i-1][sam.t[j][a[i]]],f[i-1][j]+1));
}
}
ans=f[n][0];
print(ans);
}
void four() {
int ans=inf,dian=lam.size();
memset(f,0x3f,sizeof f);
f[0][1]=0;
for (int i=1;i<=n;++i) {
f[i][1]=0;
for (int j=1;j<=dian;++j) f[i][j]=f[i-1][j];
for (int j=1;j<=dian;++j) {
up(f[i][lam.t[j][a[i]]],min(f[i-1][lam.t[j][a[i]]],f[i-1][j]+1));
}
}
ans=f[n][0];
print(ans);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("my.out","w",stdout);
#endif
n=read(a);
m=read(b);
sam.build(b,m);
lam.build(b,m);
one();
two();
three();
four();
return 0;
}

bzoj4032-最短不公共子串的更多相关文章

  1. 【BZOJ4032】[HEOI2015]最短不公共子串(后缀自动机,序列自动机)

    [BZOJ4032][HEOI2015]最短不公共子串(后缀自动机,序列自动机) 题面 BZOJ 洛谷 题解 数据范围很小,直接暴力构建后缀自动机和序列自动机,然后直接在两个自动机上进行\(bfs\) ...

  2. bzoj4032/luoguP4112 [HEOI2015]最短不公共子串(后缀自动机+序列自动机上dp)

    bzoj4032/luoguP4112 [HEOI2015]最短不公共子串(后缀自动机+序列自动机上dp) bzoj Luogu 题解时间 给两个小写字母串 $ A $ , $ B $ ,请你计算: ...

  3. bzoj4032: [HEOI2015]最短不公共子串(SAM+DP)

    4032: [HEOI2015]最短不公共子串 题目:传送门 题解: 陈年老题良心%你赛膜爆嘎爷 当初做题...一眼SAM...结果只会两种直接DP的情况... 情况1: 直接设f[i][j] 表示的 ...

  4. BZOJ 4032: [HEOI2015]最短不公共子串

    4032: [HEOI2015]最短不公共子串 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 446  Solved: 224[Submit][Sta ...

  5. 洛谷 P4112 [HEOI2015]最短不公共子串 解题报告

    P4112 [HEOI2015]最短不公共子串 题目描述 在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之. 一个串的"子串"指的是它的连续的一段,例如bcd是 ...

  6. BZOJ 4032: [HEOI2015]最短不公共子串 后缀自动机 暴力

    4032: [HEOI2015]最短不公共子串 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4032 Description 在虐各种最 ...

  7. luoguP4112 [HEOI2015]最短不公共子串 SAM,序列自动机,广搜BFS

    luoguP4112 [HEOI2015]最短不公共子串 链接 luogu loj 思路 子串可以用后缀自动机,子序列可以用序列自动机. 序列自动机是啥,就是能访问到所有子序列的自动机. 每个点记录下 ...

  8. BZOJ4032[HEOI2015]最短不公共子串——序列自动机+后缀自动机+DP+贪心

    题目描述 在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之. 一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是. 一个串的“子序列”指的是它的可以 ...

  9. BZOJ4032:[HEOI2015]最短不公共子串(SAM)

    Description 在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之. 一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是. 一个串的“子序列” ...

  10. [BZOJ4032][HEOI2015]最短不公共子串(Trie+DP)

    在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之——被它们虐. 操作一:对A,B分别建SAM,暴力BFS. 操作二:对B建序列自动机或SAM,A在上面暴力匹配. 操作三:对A,B建 ...

随机推荐

  1. 20155306 《信息安全技术概论》实验二 Windows口令破解

    20155306 <信息安全技术概论>实验二 Windows口令破解 [实验目的] 了解Windows口令破解原理 对信息安全有直观感性认识 能够运用工具实现口令破解 [实验人数] 每组1 ...

  2. 系统分析与设计——WordCount

    成果: https://gitee.com/ZMLJZ/codes/0k19q4upgmrfde265l7vx36 作业要求: 根据WordCount的需求描述,先编程实现,再编写单元测试,最后撰写博 ...

  3. eclipse中编译出现错误undefined reference to `_sbrk'

    1. 在eclipse中使用gcc-arm-none-eabi-7-2017-q4-major-win32编译代码的时候出现了undefined reference to `_sbrk' e:/pro ...

  4. hdu2065"红色病毒"问题(指数母函数+快速幂取模)

    "红色病毒"问题 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Other ...

  5. 【转】微信小程序实现自动化测试

    山雨欲来风满楼,最近微信小程序相关开发文章吹遍大江南北,亦有摧枯拉朽万象更新之势.问小程序形为何物,直教IT众生怡情悦性高潮迭起.作为一名有着远大理想“包袱”与互联网变革 “使命感”的测试工程师,我再 ...

  6. hadoop 基础入门

    启动:   格式化节点:bin/hdfs namenode -format   全部启动:sbin/start-dfs:datanode.namenode                    sbi ...

  7. js-jQuery对象与dom对象相互转换(转载)

    核心提示:jquery选择器得到的jquery对象和标准的 javascript中的document.getElementById()取得的dom对象是两种不同的对象类型,一般情况下,如S(’#id’ ...

  8. Laxcus大数据操作系统2.0(5)- 第二章 数据组织

    第二章 数据组织 在数据的组织结构设计上,Laxcus严格遵循数据和数据描述分离的原则,这个理念与关系数据库完全一致.在此基础上,为了保证大规模数据存取和计算的需要,我们设计了大量新的数据处理技术.同 ...

  9. 转:为什么说招到合适的人比融到钱更加重要 - Hiring Great Talent is More Important Than Fund Raising

    我在猎头行业工作了 20 多年,一直在帮助创业公司招聘优秀的人才.我服务过的客户既有 VC 投资的初创企业,也有即将 IPO 的公司.我和 200 多个 VC 合作过,也见过 300 多个客户失败的案 ...

  10. IT工具使用

    linux 其他知识目录 常用快捷键总结 博客view  code 删除,先删除,再清除格式