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

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 446  Solved: 224
[Submit][Status][Discuss]

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

 

Source

 

[Submit][Status][Discuss]

本来以为是HEOI2015的大水题,然后被小甜甜安利了一发序列自动机……

我等蒟蒻并不会此等算法,所以只好暴力喽!

拿到之后看到是四个答案,就分别考虑。

问题一 A的一个最短的子串,不是B的子串

这个比较简单哈,刺姐姐和任哥哥不约而同地给出了HASH做法——预先对B串的每个子串处理出HASH值,扔进哈希表里,然后暴力枚举A串的每个子串,查询是否在B中出现过,如果没有出现过,就可以用来更新答案。

然后蒟蒻的我表示并不会HASH此等算法,只好KMP暴力喽。每次枚举A串中的一个位置,作为其子串的左端点,记为$L$。此时我们希望查询所有以$L$作为左端点的A的子串,最短的一个不是B的子串的东东。这个就对A串的$[L,Len]$求next数组,拿B串跑一遍KMP就行了。时间复杂度$O(N^2)$。

问题二 A的一个最短的子串,不是B的子序列

这个非常简单哈,众神犇(除了我的所有人)一致给出了暴力做法。枚举A的子串的左端点,然后暴力检查至少以那个点作为右端点,该子串才不是B的子序列。时间复杂度$O(N^2)$。

问题三 A的一个最短的子序列,不是B的子串

这个也很简单哈,众神犇表示不直接序列自动机直接上就可以,但是蒟蒻的我依旧不会,只好写暴力了哈。

先取出B的所有子串,塞到Trie树里面去,总的节点数是$O(N^2)$,算了下内存有点吃紧,就用map<int,int>了。

然后在每个节点上记录一个标记mark,代表A串中最少选出几个字符,才能匹配到Trie上的这个节点来。这个直接DFS就可以得到了。

然后在在每个Trie树节点上枚举一个字符,如果该节点没有这个字符的出边,那么A串就有机会找出一个合法的解了。此时我们只需要知道A串在mark位置之后是否出现过字符c即可,这个很简单喽。

问题四 A的一个最短的子序列,不是B的子序列

这个最最简单哈,只要用$f_{i,j}$表示使用A的前$i$个字符,使得B不得不使用前$j$个字符和其匹配,所能使用的最少字符数。这个$O(N^2)$动态规划,太简单就不说了。

然后顺利用四种暴力水过HEOI2015的最水一题。

 #include <map>
#include <cstdio>
#include <cstring> #define chr char
#define rst register
#define rnt register int template <class T>
inline T min(const T &a, const T &b)
{
return a < b ? a : b;
} template <class T>
inline T max(const T &a, const T &b)
{
return a > b ? a : b;
} #define mxn 2005
#define mxm 4000005 int n, m; chr a[mxn];
chr b[mxn]; namespace case1
{
int tot;
int pos;
int vis[mxn];
int fil[mxn];
int nxt[mxn][]; inline int solve(int s)
{
tot = ;
pos = ; memset(vis, , sizeof vis);
memset(nxt, , sizeof nxt);
memset(fil, , sizeof fil); for (rnt i = s; i < n; ++i)
pos = nxt[pos][a[i] - 'a'] = ++tot; fil[] = ; for (rnt i = ; i < ; ++i)
if (nxt[][i])
fil[nxt[][i]] = ;
else
nxt[][i] = ; for (rnt i = ; i <= tot; ++i)
for (rnt j = ; j < ; ++j)
if (nxt[i][j])
fil[nxt[i][j]] = nxt[fil[i]][j];
else
nxt[i][j] = nxt[fil[i]][j]; pos = ; for (rnt i = ; i < m; ++i)
vis[pos = nxt[pos][b[i] - 'a']] = ; for (rnt i = ; i <= tot; ++i)
if (!vis[i])return i - ; return 1E9;
} inline void main(void)
{
int ans = 1E9; for (rnt i = ; i < n; ++i)
ans = min(ans, solve(i)); if (ans != 1E9)
printf("%d\n", ans);
else
printf("%d\n", -);
}
} namespace case2
{
inline int solve(int s)
{
for (rnt i = s, j = ; i < n; ++i, ++j)
{
while (j < m && b[j] != a[i])
++j; if (j >= m)
return i - s + ;
} return 1E9;
} inline void main(void)
{
int ans = 1E9; for (rnt i = ; i < n; ++i)
ans = min(ans, solve(i)); if (ans != 1E9)
printf("%d\n", ans);
else
printf("%d\n", -);
}
} namespace case3
{
int nxt[mxn][]; inline void prework(void)
{
for (rnt i = ; i <= n; ++i)
for (rnt j = ; j < ; ++j)
nxt[i][j] = n; for (rnt i = ; i < n; ++i)
nxt[i][a[i] - 'a'] = i; for (rnt i = n - ; i >= ; --i)
for (rnt j = ; j < ; ++j)
nxt[i][j] = min(nxt[i][j], nxt[i + ][j]);
} typedef std::map<int, int> map;
typedef std::map<int, int>::iterator itr; int tot = ;
int mrk[mxm];
map son[mxm]; inline void build(void)
{
for (rnt i = ; i < m; ++i)
{
rnt t = ; for (rnt j = i; j < m; ++j)
{
rnt c = b[j] - 'a'; if (son[t][c] == )
son[t][c] = ++tot; t = son[t][c];
}
}
} int ans = 1E9; inline void getmark(int t = , int d = )
{
if (d >= ans)return; int p = mrk[t]; for (rnt i = ; i < ; ++i)
if (nxt[p][i] < n) {
if (son[t][i])
mrk[son[t][i]] = nxt[p][i] + ,
getmark(son[t][i], d + );
else
ans = min(ans, d);
}
} inline void main(void)
{
build(); prework(); getmark(); if (ans != 1E9)
printf("%d\n", ans);
else
printf("%d\n", -);
}
} namespace case4
{
int nxt[mxn][]; int len[mxn][mxn]; inline void main(void)
{
for (rnt i = ; i <= m; ++i)
for (rnt j = ; j < ; ++j)
nxt[i][j] = m; for (rnt i = ; i < m; ++i)
nxt[i][b[i] - 'a'] = i; for (rnt i = m - ; i >= ; --i)
for (rnt j = ; j < ; ++j)
nxt[i][j] = min(nxt[i][j], nxt[i + ][j]); memset(len, 0x3f, sizeof len); len[][] = ; rnt t; for (rnt i = ; i < n; ++i)
for (rnt j = ; j <= m; ++j)
if (len[i][j] < 0x3f3f3f3f) {
t = nxt[j][a[i] - 'a'] + ;
len[i + ][j] = min(len[i + ][j], len[i][j]);
len[i + ][t] = min(len[i + ][t], len[i][j] + );
} int ans = 1E9; for (rnt i = ; i <= n; ++i)
ans = min(ans, len[i][m + ]); if (ans != 1E9)
printf("%d\n", ans);
else
printf("%d\n", -);
}
} signed main(void)
{
#ifndef ONLINE_JUDGE
freopen("in", "r", stdin);
freopen("out", "w", stdout);
#endif scanf("%s", a); n = strlen(a);
scanf("%s", b); m = strlen(b); case1::main();
case2::main();
case3::main();
case4::main();
}

@Author: YouSiki

BZOJ 4032: [HEOI2015]最短不公共子串的更多相关文章

  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]最短不公共子串(DP 后缀自动机)

    题目链接 1.求A的最短子串,它不是B的子串. 子串是连续的,对B建SAM,枚举起点,在SAM上找到第一个无法匹配点即可.O(n)用SAM能做吗..开始想错了. 2.求A的最短子串,它不是B的子序列. ...

  4. 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] ...

  5. BZOJ 4032: [HEOI2015]最短不公共子串 (dp*3 + SAM)

    转博客大法好 第4个子任务中,为什么只转移最近的一个位置,自己YY吧(多YY有益身体健康). #include <bits/stdc++.h> using namespace std; t ...

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

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

  7. 【BZOJ】4032: [HEOI2015]最短不公共子串(LibreOJ #2123)

    [题意]给两个小写字母串A,B,请你计算: (1) A的一个最短的子串,它不是B的子串 (2) A的一个最短的子串,它不是B的子序列 (3) A的一个最短的子序列,它不是B的子串 (4) A的一个最短 ...

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

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

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

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

随机推荐

  1. Deep Learning(深度学习)学习笔记整理系列之(一)(转)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0  2013-0 ...

  2. 用PHP山寨一款软件

    什么是我国软件工程师引以为豪的能力?山寨.山寨,山寨! 我国程序员的山寨能力是世界一流的.这一点在世界范围内令人闻风丧胆.世界上根本就找不到一款我国工程师不能山寨的软件. 今天,锋哥教大家来山寨一款软 ...

  3. 利用Git工具将本地创建的项目上传到Github上

    前言 作为一个对前沿技术很看好的小青年,怎么能不会用Github呢?一年前我创建了Github,也知道git,但是尝试过用,但是就没弄明白,很多粉丝都问我Github的账号,想关注一波,无奈里面啥都没 ...

  4. CAD2020下载安装AutoCAD2020中文版下载地址+安装教程

    AutoCAD2020中文版为目前最新软件版本,我第一时间拿到软件进行安装测试,确保软件正常安装且各项功能正常可以使用,立刻拿出来分享,想用最新版本的话,抓紧下载使用吧: 我把我用的安装包贡献给你下载 ...

  5. Git版本控制器使用总结性梳理

    Git为何物?Git 是什么?大家肯定会说不就是版本控制器嘛,是的Git是目前世界上最先进的分布式版本控制系统(没有之一).1)那什么是版本控制器?举个简单的例子,比如我们用Word写文章,那你一定有 ...

  6. jsp的自定义标签

    1.相对于JSTL或Spring等第三方标签库而言的,用来实现项目中特定的功能需求. 2.自定义标签基本的组成部分 ①页面上看得见的部分 [1]通过taglib引入标签库 [2]标签本身 ②xxx.t ...

  7. JsTree使用一例

    SearchDesignPatent.treeContainer().jstree({ 'core' : { 'data' : json.data }, }).bind('click.jstree', ...

  8. Docker Compose 容器编排

    1. 前言 Docker Compose 是 Docker 容器进行编排的工具,定义和运行多容器的应用,可以一条命令启动多个容器. 使用Compose 基本上分为三步: Dockerfile 定义应用 ...

  9. mysql distinct 去重

    在使用MySQL时,有时需要查询出某个字段不重复的记录,这时可以使用mysql提供的distinct这个关键字来过滤重复的记录,但是实际中我们往往用distinct来返回不重复字段的条数(count( ...

  10. 一个ip对应多个域名多个ssl证书配置-Nginx实现多域名证书HTTPS

    一台服务器,两个域名 首先购买https,获取到CA证书,两个域名就得到两套证书 第二步:现在就是Nginx和OpenSSL的安装与配置(这里注意,一般情况下一个IP只支持一个SSL证书,那么我们现在 ...