【BZOJ】4032: [HEOI2015]最短不公共子串(LibreOJ #2123)
【题意】给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列
不存在输出-1,1<=len(A),len(B)<=2000。
【算法】后缀自动机+序列自动机
【题解】虽然网上题解很多,但我总觉得这四个问题其实可以一个统一的形式来回答。因为字符串的自动机本质是相同的。
对串B建立后缀自动机来识别子串,建立序列自动机来识别子序列,从左到右枚举A串并在B自动机上进行。(序列自动机没有fail边,但这里不需要)
先考虑识别串A的子序列,设$f_x$表示自动机中节点x识别到的A的最短子序列。
对于A的子序列,从左到右枚举当前字母c,对B自动机中的每个节点都进行转移,假设x+c=y,那么:
$$f_y=min\{ f_y,f_x+1\}$$
如果y=null,那么贡献答案$ans=min\{ ans,f_x+1\}$。
原理是:字母c可以接在自动机识别了的所有子序列的后面形成新的子序列。
这里要注意更新顺序,为了满足无后效性,序列自动机要从后往前更新,后缀自动机要按Parent树从下往上更新(trans边不可能返祖)。
在考虑识别串A的子串,c只能接在所有以c前一位结尾的子串后面,那么只要每次转移到$f_y$时初始化$f_x=inf$即可。另外注意根节点不能置为inf(要接新子串)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=,inf=0x3f3f3f3f;
int n,m,last,size,root,pre[maxn],ch[maxn][],f[maxn*],w[maxn],b[maxn*];
char s[maxn],a[maxn];
struct tree{int len,fa,t[];}t[maxn*];//
void insert_SAM(int c){
int np=++size;
t[np].len=t[last].len+;
int x=last;
last=np;
while(x&&!t[x].t[c])t[x].t[c]=np,x=t[x].fa;
if(!x)t[np].fa=root;else{
int y=t[x].t[c];
if(t[y].len==t[x].len+)t[np].fa=y;else{
int nq=++size;
t[nq]=t[y];//
t[nq].len=t[x].len+;
t[nq].fa=t[y].fa;t[y].fa=t[np].fa=nq;
while(x&&t[x].t[c]==y)t[x].t[c]=nq,x=t[x].fa;//
}
}
}
void build(){
last=size=root=;
for(int i=;i<=m;i++)insert_SAM(s[i]-'a');
for(int i=;i<=m;i++){
int c=s[i]-'a';
for(int j=i-;j>=pre[c];j--)ch[j][c]=i;
pre[c]=i;
}
for(int i=;i<=size;i++)w[t[i].len]++;
for(int i=;i<=m;i++)w[i]+=w[i-];
for(int i=;i<=size;i++)b[w[t[i].len]--]=i;
}
int trans(int x,int c,int y){
if(!y)return t[x].t[c];
else return ch[x][c];
}
void solve(int A,int B){
memset(f,0x3f,sizeof(f));
f[B^]=;
int ans=inf;
for(int i=;i<=n;i++){
int c=a[i]-'a';
for(int z=(B?m:size);z>=(B^);z--){
int x=B?z:b[z];
int y=trans(x,c,B);
if(!y)ans=min(ans,f[x]+);else{
f[y]=min(f[y],f[x]+);if(!A&&x!=(B^))f[x]=inf;
}
}
}
printf("%d\n",ans==inf?-:ans);
}
int main(){
scanf("%s%s",a+,s+);n=strlen(a+);m=strlen(s+);
build();
solve(,);solve(,);solve(,);solve(,);
return ;
}
【BZOJ】4032: [HEOI2015]最短不公共子串(LibreOJ #2123)的更多相关文章
- BZOJ 4032: [HEOI2015]最短不公共子串
4032: [HEOI2015]最短不公共子串 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 446 Solved: 224[Submit][Sta ...
- BZOJ 4032: [HEOI2015]最短不公共子串 后缀自动机 暴力
4032: [HEOI2015]最短不公共子串 题目连接: http://www.lydsy.com/JudgeOnline/problem.php?id=4032 Description 在虐各种最 ...
- bzoj 4032 [HEOI2015]最短不公共子串——后缀自动机
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4032 不是 b 的子串的话就对 b 建后缀自动机,在 a 上枚举从每个位置开始的子串或者找子 ...
- BZOJ.4032.[HEOI2015]最短不公共子串(DP 后缀自动机)
题目链接 1.求A的最短子串,它不是B的子串. 子串是连续的,对B建SAM,枚举起点,在SAM上找到第一个无法匹配点即可.O(n)用SAM能做吗..开始想错了. 2.求A的最短子串,它不是B的子序列. ...
- bzoj 4032: [HEOI2015]最短不公共子串【dp+SAM】
第一.二问: 就是最小的最长公共长度+1,设f[i][j]为a匹配到i,b匹配到j,第一问的转移是f[i][j]=(a[i]==b[j]?f[i-1][j-1]+1:0),第二问的转移是f[i][j] ...
- BZOJ 4032: [HEOI2015]最短不公共子串 (dp*3 + SAM)
转博客大法好 第4个子任务中,为什么只转移最近的一个位置,自己YY吧(多YY有益身体健康). #include <bits/stdc++.h> using namespace std; t ...
- BZOJ 4032: [HEOI2015]最短不公共子串(后缀自动机+记忆化搜索)
传送门 解题思路 首先需要预处理两个串\(nxt(i)(j)\)表示i位置之后最近的\(j\). 第一问直接对\(b\)建后缀自动机,枚举\(a\)的起点暴力匹配. 第二问枚举\(a\)的起点,\(b ...
- bzoj4032: [HEOI2015]最短不公共子串(SAM+DP)
4032: [HEOI2015]最短不公共子串 题目:传送门 题解: 陈年老题良心%你赛膜爆嘎爷 当初做题...一眼SAM...结果只会两种直接DP的情况... 情况1: 直接设f[i][j] 表示的 ...
- 【BZOJ4032】[HEOI2015]最短不公共子串(后缀自动机,序列自动机)
[BZOJ4032][HEOI2015]最短不公共子串(后缀自动机,序列自动机) 题面 BZOJ 洛谷 题解 数据范围很小,直接暴力构建后缀自动机和序列自动机,然后直接在两个自动机上进行\(bfs\) ...
随机推荐
- Visual C++ 8.0对象布局
哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout和/d1reportAllClassLayout ...
- iOS- 关于AVAudioSession的使用——后台播放音乐
1.前言 •AVAudioSession是一个单例,无需实例化即可直接使用.AVAudioSession在各种音频环境中起着非常重要的作用 •针对不同的音频应用场景,需要设置不同的音频会话分类 1 ...
- virtualbox 5.0.6 在debian jessie amd64启动报错
通过dmesg发现vboxdrv启动报错: [ 18.844888] systemd[1]: [/lib/systemd/system/vboxdrv.service:5] Failed to add ...
- JS高级 2
递归:函数自己调用自己 在JavaScript中唯一能产生作用域的东西是 函数!js中只有函数可以创建作用域 词法作用域,也叫做静态作用域 //就是在代码写好的那一刻,变量和函数的作用域就已经确定了, ...
- phpMyadmin导入导出数据中出现的错误处理
1 2
- Spring 学习7 -事务
1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...
- HDU2993_MAX Average Problem
题目要求你在n个数的序列中,找出一段连续的长度不小于k的连续的序列,使得这个序列的平均数最大.输出这个平均数. 典型的优先队列.首先我们需要根据输入的序列,制造一个和序列. 然后从k开始往后面走,其实 ...
- [九]SpringBoot 之 定时任务
代码: package me.shijunjie.config; import org.springframework.context.annotation.Configuration; import ...
- C++解析(1):C到C++的升级
0.目录 1.C与C++的关系 2.C到C++的升级 2.1 语言的实用性 2.2 register关键字 2.3 同名的全局变量 2.4 struct关键字 2.5 int f() 与 int f( ...
- Fibsieve`s Fantabulous Birthday LightOJ - 1008(找规律。。)
Description 某只同学在生日宴上得到了一个N×N玻璃棋盘,每个单元格都有灯.每一秒钟棋盘会有一个单元格被点亮然后熄灭.棋盘中的单元格将以图中所示的顺序点亮.每个单元格上标记的是它在第几秒被点 ...