Description

在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。

一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。
一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。
下面,给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列

Input

有两行,每行一个小写字母组成的字符串,分别代表A和B。

Output

输出4行,每行一个整数,表示以上4个问题的答案的长度。如果没有符合要求的答案,输出-1.

Sample Input

aabbcc
abcabc

Sample Output

2
4
2
4

HINT

对于100%的数据,A和B的长度都不超过2000

Solution

强行四合一?

(一)枚举$A$串的左端点,然后从左端点开始往后在$B$串的$SAM$上面跑,一旦失配就更新答案然后$break$

(二)预处理数组$next[i][j]$表示从$i$后面的第一次出现字母$j$的位置。预处理出$nextA$和$nextB$,然后枚举$A$左端点往后贪心,如果失配就更新答案然后$break$

(三)设$len[i]$表示在$B$串的$SAM$的$i$点的时候最短的长度。然后用$A$串在$B$的$SAM$上面跑。如果失配就更新答案,否则就更新$len$。

(四)设$len[i]$表示在$B$串的$i$位置的时候最短的长度。然后用$A$串的每一个字母,借$next$数组倒序去更新$len$。如果失配就更新答案,否则就更新$len$。至于为什么要倒序,其实是和背包差不多的原理,并不难想。

Code

 #include<iostream>
#include<cstring>
#include<cstdio>
#define N (4009)
using namespace std; char s[N],t[N];
int slen,tlen,nextA[N][],nextB[N][],last[],len[N]; struct SAM
{
int son[N][],fa[N],step[N],wt[N],od[N];
int p,q,np,nq,last,cnt;
SAM(){last=cnt=;} void Insert(int x)
{
p=last; np=last=++cnt; step[np]=step[p]+;
while (p && !son[p][x]) son[p][x]=np, p=fa[p];
if (!p) fa[np]=;
else
{
q=son[p][x];
if (step[q]==step[p+]) fa[np]=q;
else
{
nq=++cnt; step[nq]=step[p]+;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q]; fa[np]=fa[q]=nq;
while (son[p][x]==q) son[p][x]=nq, p=fa[p];
}
}
}
}SAM; void CalcNext()
{
memset(last,-,sizeof(last));
for (int i=slen; i>=; --i)
{
for (int j=; j<; ++j) nextA[i][j]=last[j];
last[s[i]-'a']=i;
}
memset(last,-,sizeof(last));
for (int i=tlen; i>=; --i)
{
for (int j=; j<; ++j) nextB[i][j]=last[j];
last[t[i]-'a']=i;
}
} void Sub1()
{
int ans=2e9;
for (int i=; i<=slen; ++i)
{
int now=;
for (int j=i; j<=slen; ++j)
{
if (!SAM.son[now][s[j]-'a']) {ans=min(ans,j-i+); break;}
now=SAM.son[now][s[j]-'a'];
}
}
printf("%d\n",ans==2e9?-:ans);
} void Sub2()
{
int ans=2e9;
for (int i=; i<=slen; ++i)
{
int now=;
for (int j=i; j<=slen; ++j)
{
if (nextB[now][s[j]-'a']==-) {ans=min(ans,j-i+); break;}
now=nextB[now][s[j]-'a'];
}
}
printf("%d\n",ans==2e9?-:ans);
} void Sub3()
{
int ans=2e9;
memset(len,0x7f,sizeof(len));
len[]=;
for (int i=; i<=slen; ++i)
for (int j=; j<=SAM.cnt; ++j)
{
int nxt=SAM.son[j][s[i]-'a'];
if (!nxt) ans=min(ans,len[j]);
else len[nxt]=min(len[nxt],len[j]+);
}
printf("%d\n",ans==2e9?-:ans);
} void Sub4()
{
int ans=2e9;
memset(len,0x7f,sizeof(len));
len[]=;
for (int i=; i<=slen; ++i)
for (int j=tlen; j>=; --j)
{
int nxt=nextB[j][s[i]-'a'];
if (nxt==-) ans=min(ans,len[j]+);
else len[nxt]=min(len[nxt],len[j]+);
}
printf("%d\n",ans==2e9?-:ans);
} int main()
{
scanf("%s%s",s+,t+);
slen=strlen(s+), tlen=strlen(t+);
for (int i=; i<=tlen; ++i)
SAM.Insert(t[i]-'a');
CalcNext();
Sub1(); Sub2(); Sub3(); Sub4();
}

BZOJ4032:[HEOI2015]最短不公共子串(SAM)的更多相关文章

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

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

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

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

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

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

  4. BZOJ4032: [HEOI2015]最短不公共子串(后缀自动机+序列自动机)

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

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

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

  6. BZOJ4032 [HEOI2015]最短不公共子串 【后缀自动机 + 序列自动机 + dp】

    题目链接 BZOJ4032 题解 首先膜\(hb\) 空手切神题 一问\(hash\),二问枚举 三问\(trie\)树,四问\(dp\) 南二巨佬神\(hb\) 空手吊打自动机 \(orz orz ...

  7. BZOJ4032 : [HEOI2015]最短不公共子串

    第一问: 对B串建立SAM,暴力枚举A的每个子串,在SAM上走,若失配则可行. 第二问: 设g[i][j]表示B串的第i个字符之后最早出现的字符j的位置,暴力枚举A的每个子串,按照g贪心地走,若失配则 ...

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

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

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

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

随机推荐

  1. Response.cookies和Request.cookies

    Response.cookies和Request.cookies的区别很重要,由于方法基本都是差不多的,特别对于初学者而言,很容易出现混淆. 简单说就是创建cookie用response,获取cook ...

  2. Button 控件

    Button 控件是由system.Windows.Forms.button类提供,该控件最常用使用就是编写处理按钮的Click事件及MouseEnter事件代码 常用属性 Text按钮的说明 Ima ...

  3. C#三大特性之 封装、继承、多态

    一.封装: 封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(我们称之为类).被封装的对象通常被称为抽象数据类型.  封装的意义: 封装的意义在于保护或者防止代码(数据) ...

  4. Hadoop学习笔记(9) ——源码初窥

    Hadoop学习笔记(9) ——源码初窥 之前我们把Hadoop算是入了门,下载的源码,写了HelloWorld,简要分析了其编程要点,然后也编了个较复杂的示例.接下来其实就有两条路可走了,一条是继续 ...

  5. 撩课-Python-每天5道面试题-第6天

    一. 函数的概念是什么? 在Python中如何定义?使用场景是什么? 概念 写了一段代码实现了某个小功能; 然后把这些代码集中到一块, 起一个名字; 下一次就可以根据这个名字再次使用这个代码块, 这就 ...

  6. Spring MVC No converter found for return value of type 解决方法

    1.在pom中添加 jackson <properties> <jackson.version>2.8.5</jackson.version> </prope ...

  7. SQL导出到Excel 存储过程

    if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_exporttb]') and OBJECTPROPER ...

  8. C#迭代器、装箱/拆箱、重载等

    迭代器 迭代器是什么? 迭代器是作为一个容器,将要遍历的数据放入,通过统一的接口返回相同类型的值. 为什么要用迭代器? 为何了为集合提供统一的遍历方式,迭代器模式使得你能够获取到序列中的所有元素而不用 ...

  9. BZOJ2229: [Zjoi2011]最小割(最小割树)

    传送门 最小割树 算法 初始时把所有点放在一个集合 从中任选两个点出来跑原图中的最小割 然后按照 \(s\) 集合与 \(t\) 集合的归属把当前集合划分成两个集合,递归处理 这样一共跑了 \(n − ...

  10. 前端面试题1(html)

    HTML     * Doctype作用?严格模式与混杂模式如何区分?它们有何意义? 1.<!DOCTYPE> 声明位于文档中的最前面的位置,处于 <html> 标签之前.此标 ...