题意:

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

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

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

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

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

解:这是什么四合一毒瘤题......

先上正解:

第一问对B建后缀自动机,枚举A的每个前缀,在B上跑。

第二问枚举A中子串的开始位置,B中贪心匹配。O(n2)。

后两问分别建出B的后缀自动机和序列自动机,然后DP,用f[i][j]表示考虑A的前i个字符,在自动机上走到节点j至少需要几步。答案是f[n][null]

转移就是看节点j有没有A[i + 1]的出边,更新。

好的,接下来上我的SB解法......

第一问,广义后缀自动机裸题。

第二问,我们只需要找出A中的一个子串,它向前/后添加一个字符时在B中无法继续匹配即可。

对A建后缀自动机。

由于这样后缀自动机一个节点的多个子串是逐渐在前面添加字符,所以考虑从后往前找子串,在B中也从后往前匹配。

每个节点有一个posA表示该节点代表的的串的结尾在A中的位置,还有一个pos表示该节点代表的最长串在在B中从后向前贪心匹配到的最右距离。

在fail树上DFS。每个点继承它父亲的pos并一个一个贪心匹配直到这个点代表的最长串完成匹配。如果失配了就更新答案。否则就记录pos,向下DFS。

第三问,首先可以得知,如果A中的每一个字符都在B中出现过,那么符合条件的A一定是B的一个子串加上一个B中没有的转移。

于是建出B的后缀自动机。

我们用pos[x]表示节点x所表示的最短子串在A中从前往后贪心匹配到的最短距离。为什么是最短呢?因为我们要越短越好 + 越靠左越好。因为这个节点的每个子串加上一个不存在的转移都会合法,所以短比长优。而靠左的更可能在A中找到B不存在的转移。

而不存在一个非最短子串的最靠左距离会左于最短子串。

于是我们分析了一大通之后发现,只要按照拓扑序更新pos,取最小值就行了。

在每个节点的pos[x]+1开始向右扫A,如果没有转移就更新答案。否则更新下一个节点的pos。

第四问实在是想不出来了,就跑去看题解,学习了一波序列自动机,用了正解写的。

 #include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue> const int N = ; struct Edge {
int nex, v;
}edge[N << ]; int top; int tr[N * ][], fail[N], len[N], tot, cnt[N], bin[N], topo[N], last;
int e[N], n, m;
char A[N], B[N]; inline void add(int x, int y) {
top++;
edge[top].v = y;
edge[top].nex = e[x];
e[x] = top;
return;
} inline void insert(char c) {
int f = c - 'a';
int p = last, np = ++tot;
last = np;
len[np] = len[p] + ;
while(p && !tr[p][f]) {
tr[p][f] = np;
p = fail[p];
}
if(!p) {
fail[np] = ;
}
else {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
fail[np] = Q;
}
else {
int nQ = ++tot;
len[nQ] = len[p] + ;
fail[nQ] = fail[Q];
fail[Q] = fail[np] = nQ;
memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
while(tr[p][f] == Q) {
tr[p][f] = nQ;
p = fail[p];
}
}
}
return;
} inline void clear() {
memset(tr, , sizeof(tr));
memset(fail, , sizeof(fail));
memset(e, , sizeof(e));
memset(len, , sizeof(len));
memset(bin, , sizeof(bin));
memset(topo, , sizeof(topo));
memset(cnt, , sizeof(cnt));
last = tot = top = ;
return;
} namespace t1 {
bool vis[N];
inline int split(int p, int f) {
int Q = tr[p][f], nQ = ++tot;
len[nQ] = len[p] + ;
fail[nQ] = fail[Q];
fail[Q] = nQ;
memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
while(tr[p][f] == Q) {
tr[p][f] = nQ;
p = fail[p];
}
return nQ;
}
inline int insert(char c, int p) {
int f = c - 'a';
if(tr[p][f]) {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
return Q;
}
return split(p, f);
}
int np = ++tot;
len[np] = len[p] + ;
while(p && !tr[p][f]) {
tr[p][f] = np;
p = fail[p];
}
if(!p) {
fail[np] = ;
}
else {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
fail[np] = Q;
}
else {
fail[np] = split(p, f);
}
}
return np;
}
void DFS(int x) {
if(vis[x] || !x) {
return;
}
vis[x] = ;
DFS(fail[x]);
return;
}
inline void solve() {
int last = ;
tot = ;
for(int i = ; i < n; i++) {
last = insert(A[i], last);
}
last = ;
for(int i = ; i < m; i++) {
last = insert(B[i], last);
}
// SAM over
int p = ;
for(int i = ; i < m; i++) {
p = tr[p][B[i] - 'a'];
DFS(p);
}
int ans = n + m;
for(int i = ; i <= tot; i++) {
if(!vis[i]) {
ans = std::min(ans, len[fail[i]] + );
}
}
printf("%d\n", (ans != n + m) ? ans : -);
return;
}
} namespace t2 {
// pos[i] Node i's last position in B
int pos[N], posA[N], ans;
std::queue<int> Q;
void DFS(int x) {
//printf("x = %d posA[x] = %d \n", x, posA[x]);
int p = pos[fail[x]] - ;
for(int i = posA[x] - len[fail[x]]; i >= posA[x] - len[x] + ; i--) {
//printf(" > i = %d \n", i);
while(p >= && B[p] != A[i]) {
//printf(" > > p = %d \n", p);
p--;
}
//printf(" > > p = %d \n", p);
if(p < ) {
ans = std::min(ans, posA[x] - i + );
return;
}
else {
pos[x] = p;
p--;
}
}
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
DFS(y);
}
return;
}
inline void solve() {
clear();
last = tot = ;
for(int i = ; i < n; i++) {
int t = tot;
insert(A[i]);
for(int j = t + ; j <= tot; j++) {
posA[j] = i;
}
}
ans = n + m;
for(int i = ; i <= tot; i++) {
add(fail[i], i);
//printf("add %d %d \n", fail[i], i);
}
pos[] = m;
DFS();
printf("%d\n", ans != n + m ? ans : -);
return;
}
} namespace t3 {
int pos[N], ans;
inline void solve() {
for(int i = ; i < m; i++) {
bin[B[i]]++;
}
for(int i = ; i < n; i++) {
if(!bin[A[i]]) {
printf("1\n");
return;
}
}
clear();
tot = last = ;
for(int i = ; i < m; i++) {
insert(B[i]);
}
for(int i = ; i <= tot; i++) {
bin[len[i]]++;
}
for(int i = ; i <= tot; i++) {
bin[i] += bin[i - ];
}
for(int i = ; i <= tot; i++) {
topo[bin[len[i]]--] = i;
}
//
memset(pos, 0x3f, sizeof(pos));
ans = n + m;
pos[] = -;
for(int a = ; a <= tot; a++) {
int x = topo[a];
for(int i = pos[x] + ; i < n; i++) {
int f = A[i] - 'a';
if(!tr[x][f]) {
ans = std::min(ans, len[fail[x]] + );
}
else {
pos[tr[x][f]] = std::min(pos[tr[x][f]], i);
}
}
}
printf("%d\n", ans != n + m ? ans : -);
return;
}
} namespace t4 {
int nex[N][], last[], f[][];
inline void exmin(int &a, int b) {
a > b ? a = b : ;
return;
}
inline void solve() {
clear();
memset(f, 0x3f, sizeof(f));
for(int i = ; i < ; i++) {
last[i] = m + ;
}
for(int i = m - ; i >= ; i--) {
for(int j = ; j < ; j++) {
nex[i + ][j] = last[j];
}
last[B[i] - 'a'] = i + ;
}
for(int j = ; j < ; j++) {
nex[][j] = last[j];
}
f[][] = ;
for(int i = ; i < n; i++) {
for(int j = ; j <= m + ; j++) {
exmin(f[i + ][j], f[i][j]);
int k = A[i] - 'a';
//printf("i %d j %d nex[j][k] %d \n", i, j, nex[j][k]);
// tr[j][f]
if(!nex[j][k]) {
continue;
}
exmin(f[i + ][nex[j][k]], f[i][j] + );
//printf("f %d %d -> f %d %d : %d \n", i, j, i + 1, nex[j][k], f[i][j] + 1);
}
}
printf("%d\n", f[n][m + ] == f[n][m + ] ? - : f[n][m + ]);
return;
}
} int main() {
scanf("%s%s", A, B);
n = strlen(A);
m = strlen(B);
t1::solve();
t2::solve();
t3::solve();
t4::solve(); return ;
}

AC代码

附:序列自动机。

对于长为n的序列,序列自动机有n + 1个节点,分别表示这n个位置和起点。每个节点都有字符集个转移边,指向该字符在它之后第一次出现的位置。fail指针指向上一个它出现的位置。

能够识别所有的子序列。

洛谷P4112 最短不公共子串的更多相关文章

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

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

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

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

  3. 洛谷 P1546 最短网络 Agri-Net

    题目链接 https://www.luogu.org/problemnew/show/P1546 题目背景 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当 ...

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

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

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

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

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

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

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

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

  8. 洛谷P1546 最短网络 Agri-Net(最小生成树,Kruskal)

    洛谷P1546 最短网络 Agri-Net 最小生成树模板题. 直接使用 Kruskal 求解. 复杂度为 \(O(E\log E)\) . #include<stdio.h> #incl ...

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

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

随机推荐

  1. required: true,el-upload :action="UploadUrl()"

    <el-form-item label="所属班级:" prop="Name" :rules="[{ required: true, messa ...

  2. Linux内核总结博客 20135332武西垚

    http://www.cnblogs.com/wuxiyao/p/5220677.htmlhttp://www.cnblogs.com/wuxiyao/p/5247571.htmlhttp://www ...

  3. jsp中获取不到servlet的cookie

    今天做登陆,发现jsp中使用document.cookie获取不到servlet生成的cookie,我们可以在浏览器的cookie文件夹中发现,servlet中生成的cookie和jsp中的生成的路径 ...

  4. keras-VGG16 猫狗分类器

    keras 原理: keras系列︱图像多分类训练与利用bottleneck features进行微调(三)https://blog.csdn.net/sinat_26917383/article/d ...

  5. NFV论文集(一)

    一 文章名称:Throughput Maximization and Resource Optimization in NFV-Enabled Networks 发表时间:2017 期刊来源:ICC: ...

  6. HDOJ2025_查找最大元素

    一道简单题 HDOJ2025_查找最大元素 #include<stdio.h> #include<stdlib.h> #include<ctype.h> #incl ...

  7. Maximal Binary Matrix CodeForces - 803A (贪心+实现)

    题目链接 题意有点坑: 给你一个N*N的矩阵,让你填入K个1,使之整个矩阵关于左上到右下的对角线对称,并且这个要求这个矩阵的字典序最大. 对矩阵的字典序的定义是从每一行的第一个元素开始比较,大着为字典 ...

  8. 通过LVM给Linux扩容

    主要参考以下两篇文章: 1:https://www.cnblogs.com/sixiweb/p/3360008.html 2:https://wenku.baidu.com/view/42deee1a ...

  9. JQuery基础-- Ajax

    基本格式: get: $.get("url",data,function(res){   #.....   }) post: $.post("url",data ...

  10. HDU 2043 密码

    http://acm.hdu.edu.cn/showproblem.php?pid=2043 Problem Description 网上流传一句话:"常在网上飘啊,哪能不挨刀啊-" ...