题目链接

1.求A的最短子串,它不是B的子串。

子串是连续的,对B建SAM,枚举起点,在SAM上找到第一个无法匹配点即可。O(n)用SAM能做吗。。开始想错了。

2.求A的最短子串,它不是B的子序列。

子序列...直接建SAM没啥用。考虑A[i]和上一位A[i-1],用f[i][j]表示到A[i]匹配到B[j]时连续的A的子串长度,如果A[i]==B[j],则f[i][j]=max{f[i-1][k]+1}(k<j)。

是取max啊,因为B会尽可能匹配。枚举完B[n]后且max{f[i][k]}!=i(能在A[i]前接一位)才能用max{f[i][k]}+1更新答案。

3.求A的最短子序列,它不是B的子串。

子序列的话就是前面的会对后面的产生影响。因为是B的子串,所以我们还是在SAM上做。

令f[p]表示匹配当SAM上的p点时 A目前最短子序列的长度。对于每个A[i],枚举点p,若p点A[i]的转移为0,那么可以用当前的f[p]更新答案;否则f[转移点]=min(f[转移点],f[p]+1)。

f[1]=0,其余为INF,只有从根节点开始走才是B的子串。

4.求A的最短子序列,它不是B的子序列。

令son[i][c]表示,当前为A[i],其后离i最近的满足A[j]==c的j在哪。转移不变,f[j]=min(f[j],f[i]+1)。

也可以建序列自动机...序列自动机理论复杂度是O(n^2)的,因为每插入一个字符,之前没有该转移的点都要与其连边。

为了不是那么暴力地枚举所有点,可以对每个字符维护一个las[c'],即上次插入c'所在位置。插入c时枚举所有c',从las[c']向上更新连边直到有转移。

注意4.的DP要倒序枚举,避免某位置被A[i]更新多次。感觉3.也需要,但实际不用,不知道为什么。。

如果建序列自动机的话方便很多,但是就是不想。。DP就DP吧。

**Update: **好像这个东西确实能拿来写暴力。。

//17224kb	528ms
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=2007; int n,m;
char A[N],B[N];
struct Suffix_Automaton
{
#define S 4007
int n,tot,las,fa[S],son[S][26],len[S]; Suffix_Automaton() {tot=las=1;}
void Insert(int c)
{
int np=++tot,p=las; len[las=np]=len[p]+1;
for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
if(!p) fa[np]=1;
else
{
int q=son[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot; len[nq]=len[p]+1;
memcpy(son[nq],son[q],sizeof son[q]);
fa[nq]=fa[q], fa[np]=fa[q]=nq;
for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
}
}
}
void Build(char *s)
{
las=tot=1, n=strlen(s+1);
for(int i=1; i<=n; ++i) Insert(s[i]-'a');
}
}sam; void Solve1()
{
int ans=N;
for(int i=1; i<=n; ++i)
for(int j=i,p=1,now=1; j<=n; ++j)
if(sam.son[p][A[j]-'a']) ++now, p=sam.son[p][A[j]-'a'];
else {ans=std::min(ans,now); break;}
printf("%d\n",ans==N?-1:ans);
}
void Solve2()
{
static int f[N][N];
int ans=N;
for(int i=1; i<=n; ++i)
{
int bef=0,mx=0;
for(int j=1; j<=m; ++j)
{
if(A[i]==B[j]) f[i][j]=bef+1;
mx=std::max(mx,f[i][j]);
bef=std::max(bef,f[i-1][j]);
}
if(mx!=i) ans=std::min(ans,mx+1);//, printf("%d %d\n",i,mx);
}
printf("%d\n",ans==N?-1:ans);
}
void Solve3()
{
static int f[S];//2N!
memset(f,0x3f,sizeof f);
f[1]=0; int ans=N;
for(int i=1; i<=n; ++i)
for(int j=1,t,tot=sam.tot; j<=tot; ++j)
if(!(t=sam.son[j][A[i]-'a'])) ans=std::min(ans,f[j]);
else f[t]=std::min(f[t],f[j]+1);
printf("%d\n",ans==N?-1:ans+1);
}
void Solve4()
{
static int f[N],son[N][26],las[26];
B[0]='a';
for(int i=m; ~i; --i)
{
for(int j=0; j<26; ++j) son[i][j]=las[j];
las[B[i]-'a']=i;
}
memset(f,0x3f,sizeof f);
f[0]=0/*0与其它都有转移 以0为根节点*/; int ans=N;
for(int i=1; i<=n; ++i)
for(int j=m,t; ~j; --j)
if(!(t=son[j][A[i]-'a'])) ans=std::min(ans,f[j]);
else f[t]=std::min(f[t],f[j]+1);
printf("%d\n",ans==N?-1:ans+1);
} int main()
{
scanf("%s%s",A+1,B+1), n=strlen(A+1), m=strlen(B+1);
sam.Build(B);
Solve1(), Solve2(), Solve3(), Solve4(); return 0;
}

BZOJ.4032.[HEOI2015]最短不公共子串(DP 后缀自动机)的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. ASP.NET MVC学习笔记-----Filter(2)

    接上篇ASP.NET MVC学习笔记-----Filter(1) Action Filter Action Filter可以基于任何目的使用,它需要实现IActionFilter接口: public ...

  2. js调试系列: 控制台命令行API

    js调试系列目录: - 上次初步介绍了什么是控制台,以及简单的 console.log 输出信息.最后还有两个小问题,我们就当回顾,来看下怎么操作吧. 先打开百度,然后按 F12 打开后,如果不是 C ...

  3. shift 用法

    shift  shift命令用于对参数的移动 (左移),通常用于在不知道传入参数个数的情况下依次遍历每个参数然后进行相应处理(常见于Linux中各种程序的启动脚本). 示例 1  示例 依次读取输入的 ...

  4. Linux那些事儿之我是Hub(大结局)挂起自动化【转】

    转自:http://blog.csdn.net/fudan_abc/article/details/1805471 目睹了当今大学校园的素质流氓化,kiss公开化,消费白领化,上课梦游化,逃课普遍化, ...

  5. .NET C# Tostring format 格式化字符串

    一.数值型 formatCode 是可选的格式化代码字符串.必须用“{”和“}”将格式与其他字符分开.如果恰好在格式中也要使用大括号,可以用连续的两个大括号表示一个大括号,即: “{{”或者“}}”. ...

  6. C/C++杂记:深入理解数据成员指针、函数成员指针

    1. 数据成员指针 对于普通指针变量来说,其值是它所指向的地址,0表示空指针. 而对于数据成员指针变量来说,其值是数据成员所在地址相对于对象起始地址的偏移值,空指针用-1表示.例: 代码示例: str ...

  7. 002_docker构建zookeeper环境

    最近因为要维护公司zk环境,所以自己先得搞一套先玩玩 git地址=>https://github.com/jplock/docker-zookeeper/tree/v3.4.9 一.build ...

  8. ssh远程免密登录Linux

    一.在本地机器创建公钥,一路回车即可 ssh-keygen -t rsa 二.发送公钥到远程服务器端 如果是默认端口:scp id_rsa.pub user@ip:~/.shh 如果远程服务器设置的是 ...

  9. java垃圾回收的回收器

    回收器的种类: --串行(–XX:+UseSerialGC ) Out ofBox算法,年轻代串行复制,年老代串行标记整理,主要用于桌面应用 --并行(–XX:+UseParallelGC ) 年轻代 ...

  10. Fiddler实现移动端手机抓包

    Fiddler是一个http调试代理,它能 够记录所有的你电脑和互联网之间的http通讯,Fiddler 可以也可以让你检查所有的http通讯,设置断点,以及Fiddle 所有的“进出”的数据(指co ...