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

bzoj Luogu

题解时间

给两个小写字母串 $ A $ , $ B $ ,请你计算:

(1) $ A $ 的一个最短的子串,它不是 $ B $ 的子串

(2) $ A $ 的一个最短的子串,它不是 $ B $ 的子序列

(3) $ A $ 的一个最短的子序列,它不是 $ B $ 的子串

(4) $ A $ 的一个最短的子序列,它不是 $ B $ 的子序列

水题四合一,一题更比四题sao

首先由于都是要从 $ A $ 中找,所以按照套路是把 $ A $ 在 $ B $ 上匹配

所以先构建出 $ B $ 的SAM和序列自动机。

序列自动机:就是一个 $ next[i][ch] $ 数组表示第 $ i $ 位往后第一个出现字符 $ ch $ 的位置,纯粹用来匹配的。

(1) 直接以 $ A $ 每一位为起点在 $ B $ 的SAM上暴力匹配,只要失配就更新答案然后跳出。

(2) 换成在序列自动机上。

(3) $ dp[i][j] $ 表示 $ A $ 的前 $ i $ 个字符形成的子序列在 $ B $ 的SAM上匹配到了 $ j $ 位置,此时形成的最短子序列长度,这是个挺经典的dp了。

(4) 换成在序列自动机上。

#include<bits/stdc++.h>
using namespace std;
namespace RKK
{
const int N=2011,inf=0x3f3f3f3f;
int n1;char s1[N];
int n2;char s2[N];
struct remilia{int tranc[26],len,pre;};
struct sakuya
{
remilia s[N<<1];
int fin,size;
sakuya(){fin=size=1;}
void ins(int ch)
{
int npx,npy,lpx,lpy;
npx=++size;
s[npx].len=s[fin].len+1;
for(lpx=fin;lpx&&!s[lpx].tranc[ch];lpx=s[lpx].pre) s[lpx].tranc[ch]=npx;
if(!lpx) s[npx].pre=1;
else
{
lpy=s[lpx].tranc[ch];
if(s[lpy].len==s[lpx].len+1) s[npx].pre=lpy;
else
{
npy=++size;
s[npy]=s[lpy];
s[npy].len=s[lpx].len+1;
s[lpy].pre=s[npx].pre=npy;
while(s[lpx].tranc[ch]==lpy)
{
s[lpx].tranc[ch]=npy;
lpx=s[lpx].pre;
}
}
}
fin=npx;
}
void insert(char *str,int sl)
{
for(int i=1;i<=sl;i++)
ins(str[i]-'a');
}
}sam;
struct flandre
{
int ne[N][26],tmp[26];
void insert(char *s,int sl)
{
for(int i=sl;i>=0;i--)
memcpy(ne[i],tmp,104),tmp[s[i]-'a']=i;
}
}sem;
void solve1()
{
int ans=inf;
for(int sp=1;sp<=n1;sp++)
{
int px=1;
for(int i=sp;i<=n1;i++)
{
if(!sam.s[px].tranc[s1[i]-'a']){ans=min(ans,i-sp+1);break;}
px=sam.s[px].tranc[s1[i]-'a'];
}
}
printf("%d\n",ans==inf?-1:ans);
}
void solve2()
{
int ans=inf;
for(int sp=1;sp<=n1;sp++)
{
int px=0;
for(int i=sp;i<=n1;i++)
{
if(!sem.ne[px][s1[i]-'a']){ans=min(ans,i-sp+1);break;}
px=sem.ne[px][s1[i]-'a'];
}
}
printf("%d\n",ans==inf?-1:ans);
}
int dp[N<<1];
void solve3()
{
int ans=inf;
memset(dp,0x3f,sizeof(dp));
dp[1]=0;
for(int i=1;i<=n1;i++)
{
for(int x=sam.size;x;x--)
{
if(sam.s[x].tranc[s1[i]-'a']) dp[sam.s[x].tranc[s1[i]-'a']]=min(dp[sam.s[x].tranc[s1[i]-'a']],dp[x]+1);
else ans=min(ans,dp[x]+1);
}
}
printf("%d\n",ans==inf?-1:ans);
}
void solve4()
{
int ans=inf;
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n1;i++)
{
for(int x=n2;x>=0;x--)
{
if(!sem.ne[x][s1[i]-'a']) ans=min(ans,dp[x]+1);
else dp[sem.ne[x][s1[i]-'a']]=min(dp[sem.ne[x][s1[i]-'a']],dp[x]+1);
}
}
printf("%d\n",ans==inf?-1:ans);
}
int Iris()
{
scanf("%s%s",s1+1,s2+1),n1=strlen(s1+1),n2=strlen(s2+1);
sam.insert(s2,n2),sem.insert(s2,n2);
solve1(),solve2(),solve3(),solve4();
return 0;
}
}
int main(){return RKK::Iris();}

bzoj4032/luoguP4112 [HEOI2015]最短不公共子串(后缀自动机+序列自动机上dp)的更多相关文章

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

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

  2. bzoj 4032 [HEOI2015]最短不公共子串——后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4032 不是 b 的子串的话就对 b 建后缀自动机,在 a 上枚举从每个位置开始的子串或者找子 ...

  3. BZOJ 4032: [HEOI2015]最短不公共子串(后缀自动机+记忆化搜索)

    传送门 解题思路 首先需要预处理两个串\(nxt(i)(j)\)表示i位置之后最近的\(j\). 第一问直接对\(b\)建后缀自动机,枚举\(a\)的起点暴力匹配. 第二问枚举\(a\)的起点,\(b ...

  4. bzoj 4032 [ HEOI 2015 ] 最短不公共子串 —— 后缀自动机+序列自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4032 序列自动机其实就是每个位置记录一下某字母后面第一个出现位置,为了子序列能尽量长. 对字 ...

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 以鶸ice为例,手撸一个解释器(一)明确目标

    代码地址 # HelloWorld.ice print("hello, world") 前言(废话) 其实从开始学习编译原理到现在已经有快半年的时间了,但是其间常常不能坚持看下去龙 ...

  2. IDA FLIRT使用

    IDA FLIRT/FLAIR   FLIRT是IDA提供的一种函数识别技术,即库文件快速识别与鉴定技术(Fast Library Identification and Recognition Tec ...

  3. Redis 源码简洁剖析 15 - AOF

    AOF 是什么 AOF 持久化的实现 命令追加 AOF 文件的写入和同步 AOF 文件的载入和数据还原 AOF 重写 为什么需要重写 什么是重写 如何重写 AOF 后台重写 为什么需要后台重写 带来的 ...

  4. 树莓派GPIO开发(三):蜂鸣器-PWM调节

    配置环境 系统:Raspbian11(官方64位) 设备:树莓派CM4 一.写在前面 关于PWM的基本介绍在上一篇博客已经说过了:树莓派GPIO开发(二)RGB模块-PWM调节 在RGB模块中,我们主 ...

  5. 注意!你的 Navicat 可能被下毒了...

    大家早上好,我是程序猿DD! 刚刚看到一份来自微步在线发布的威胁情报通报,其中提到了被我们广泛应用的数据库管理工具Navicat Premium被投毒消息!如果你有用过相关版本的话,可能当前正处于数据 ...

  6. uni-app支付宝支付成功代码处理,获取trade_no数据

    uni-app支付宝支付成功以后返回很多代码,我简单梳理一下,提出trade_no数据 uni.requestPayment({ provider: 'alipay', orderInfo: orde ...

  7. java高版本下各种JNDI Bypass方法复现

    目录 0 前言 1 Java高版本JNDI绕过的源代码分析 1.1 思路一的源码分析 1.2 思路二的源码分析 2 基于本地工厂类的利用方法 2.1 org.apache.naming.factory ...

  8. Linux:査看文件的详细信息

    Linux中stat是査看文件详细信息的命令. 1.命令格式: stat [选项] 文件名/目录名 2.选项: -f:査看文件所在文件系统信息,而不是査看文件信息. 3. 案例 案例1:査看文件的详细 ...

  9. 【C#基础概念】字面量 literal

    一.字面量定义 在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation).几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数.浮点数以及字符串:而有很 ...

  10. 探究Spring原理

    探究Spring原理 探究IoC原理 首先我们大致了解一下ApplicationContext的加载流程: 我们可以看到,整个过程极为复杂,一句话肯定是无法解释的,所以我们就从ApplicationC ...