题意:

求\(n\)个串的最大\(LCS\)。

思路:

把第一个串建后缀自动机,然后枚举所有串。对于每个串,求出这个串在\(i\)节点的最大匹配为\(temp[i]\)(当前串在这个节点最多取多少),然后我们求出最终所有串在\(i\)节点的匹配最小值\(mn[i]\)(即为所有串在\(i\)节点都能取到多少),答案即为\(max\{min[i]\}\)。

但是我们能发现,如果我们更新了\(temp[i]\),那么其实\(fa[i]\)的\(temp[fa[i]]\)也应该要更新,因为父节点是我的后缀子串,只是我没有走过去而已,并且\(temp[fa[i]] = max(temp[fa[i]], \ max(temp[i],\ mxlen[fa[i]]))\),因为父节点的匹配长度不能超过他本身长度。

为了能线性实现如上操作,我们按照\(mxlen\)大小桶排,因为父节点的\(mxlen\)一定小于子节点,那么我直接倒着更新就能保证我更新父节点时自己一定已经更新过了。

黄某讲的挺好,点击前往

代码:

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<bitset>
#include<string>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 10;
typedef long long ll;
const ll MOD = 1e9 + 7;
int x[maxn << 1], rk[maxn << 1];
//桶排,按照len从小到大排序节点
struct SAM{
int node[maxn << 1][26], fa[maxn << 1], mxlen[maxn << 1];
int mn[maxn << 1];
int sz, last;
int newnode(){
++sz;
memset(node[sz], 0, sizeof(node[sz]));
fa[sz] = mxlen[sz] = 0;
return sz;
}
void init(){
sz = 0;
last = newnode();
}
void insert(int k){
int p = last, np = last = newnode();
mxlen[np] = mxlen[p] + 1;
for(; p && !node[p][k]; p = fa[p])
node[p][k] = np;
if(p == 0){
fa[np] = 1;
}
else{
int t = node[p][k];
if(mxlen[t] == mxlen[p] + 1){
fa[np] = t;
}
else{
int nt = newnode();
memcpy(node[nt], node[t], sizeof(node[t]));
fa[nt] = fa[t];
mxlen[nt] = mxlen[p] + 1;
fa[np] = fa[t] = nt;
for(; p && node[p][k] == t; p = fa[p])
node[p][k] = nt;
}
}
}
void Sort(int len){
//桶排,按照len从小到大排序节点
for(int i = 1; i <= sz; i++) x[mxlen[i]]++;
for(int i = 1; i <= len; i++) x[i] += x[i - 1];
for(int i = 1; i <= sz; i++) rk[x[mxlen[i]]--] = i; for(int i = 1; i <= sz; i++) mn[i] = mxlen[i];
}
int tmp[maxn << 1];
void build(char *s){
for(int i = 0; i <= sz; i++) tmp[i] = 0;
int len = strlen(s);
int pos = 1;
int ret = 0;
for(int i = 0; i < len; i++){
int c = s[i] - 'a';
while(pos && node[pos][c] == 0){
pos = fa[pos];
ret = mxlen[pos];
}
if(pos == 0){
ret = 0;
pos = 1;
}
else{
pos = node[pos][c];
ret++;
}
tmp[pos] = max(tmp[pos], ret);
}
for(int i = sz; i >= 1; i--){
int c = rk[i];
tmp[fa[c]] = min(max(tmp[fa[c]], tmp[c]), mxlen[fa[c]]);
}
for(int i = 1; i <= sz; i++){
mn[i] = min(mn[i], tmp[i]);
}
}
void query(){
int ret = 0;
for(int i = 1; i <= sz; i++) ret = max(ret, mn[i]);
printf("%d\n", ret);
} }sam;
char s[maxn];
int main(){
sam.init();
scanf("%s", s);
int len = strlen(s);
for(int i = 0; i < len; i++) sam.insert(s[i] - 'a');
sam.Sort(len);
sam.build(s);
while(~scanf("%s", s)){
sam.build(s);
}
sam.query();
return 0;
}

SPOJ - LCS2 Longest Common Substring II(后缀自动机)题解的更多相关文章

  1. SPOJ LCS2 - Longest Common Substring II 后缀自动机 多个串的LCS

    LCS2 - Longest Common Substring II no tags  A string is finite sequence of characters over a non-emp ...

  2. SPOJ LCS2 Longest Common Substring II ——后缀自动机

    后缀自动机裸题 #include <cstdio> #include <cstring> #include <iostream> #include <algo ...

  3. SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机、状压DP)

    手动博客搬家: 本文发表于20181217 23:54:35, 原地址https://blog.csdn.net/suncongbo/article/details/85058680 人生第一道后缀自 ...

  4. 【SPOJ】Longest Common Substring(后缀自动机)

    [SPOJ]Longest Common Substring(后缀自动机) 题面 Vjudge 题意:求两个串的最长公共子串 题解 \(SA\)的做法很简单 不再赘述 对于一个串构建\(SAM\) 另 ...

  5. spoj 1812 LCS2 - Longest Common Substring II (后缀自己主动机)

    spoj 1812 LCS2 - Longest Common Substring II 题意: 给出最多n个字符串A[1], ..., A[n], 求这n个字符串的最长公共子串. 限制: 1 < ...

  6. SPOJ LCS2 - Longest Common Substring II

    LCS2 - Longest Common Substring II A string is finite sequence of characters over a non-empty finite ...

  7. SPOJ LCS2 - Longest Common Substring II 字符串 SAM

    原文链接http://www.cnblogs.com/zhouzhendong/p/8982484.html 题目传送门 - SPOJ LCS2 题意 求若干$(若干<10)$个字符串的最长公共 ...

  8. [SPOJ1812]Longest Common Substring II 后缀自动机 多个串的最长公共子串

    题目链接:http://www.spoj.com/problems/LCS2/ 其实两个串的LCS会了,多个串的LCS也就差不多了. 我们先用一个串建立后缀自动机,然后其它的串在上面跑.跑的时候算出每 ...

  9. SPOJ LCS Longest Common Substring(后缀自动机)题解

    题意: 求两个串的最大\(LCS\). 思路: 把第一个串建后缀自动机,第二个串跑后缀自动机,如果一个节点失配了,那么往父节点跑,期间更新答案即可. 代码: #include<set> # ...

随机推荐

  1. MySQL全面瓦解20:可编程性之流程控制语句

    背景 说到流程控制语句,我们在程序语法中用的比较多,比如C#的if..else...,while...,?: 等.同样的,在MySQL中,也有一些流程控制的语法,方便我们在写函数.存储过程的时候对逻辑 ...

  2. uni-app开发经验分享十二: Android平台应用启动时读写手机存储、访问设备信息(如IMEI)等权限策略及提示信息

    Android平台从6.0(API23)开始系统对权限的管理更加严格,所有涉及敏感权限都需要用户授权允许才能获取.因此一些应用基础业务逻辑需要的权限会在应用启动时申请,并引导用户允许. 读写手机存储权 ...

  3. C语言中二维数组声明时,探究省略第一维的原因

    我们在使用二维数组作为参数时,我们既可以指明这个数组各个维度的维数,同时我们也可以省略一维,但是二维却不能省略.why呢?由于编译器原理的限制,在一个数组Elemtype test[m][n]中,访问 ...

  4. 前端面试之CSS权重问题!

    前端面试之CSS权重问题! 下面的权重按照从小到大来排列! 1.通用选择器(*) 2.元素(类型)选择器 权重1 3.类选择器 权重10 4.属性选择器 5.伪类 6.ID 选择器 权重100 7.内 ...

  5. 《Effective C#》之减少装箱和拆箱

    <Effective C#>之减少装箱和拆箱_天极网 http://dev.yesky.com/msdn/359/3486359.shtml <Effective C#>之减少 ...

  6. 编译安装 codeblocks 20.03 mips64el

    期末考试要用哦,不然谁会愿意去踩这么多坑. qaq 龙梦 Fedora28 中有 codeblocks 17.12,但是 Ctrl-v 粘贴会闪退,导致压根不能用.Bing了一下发现这其实是 code ...

  7. 如何用RabbitMQ实现延迟队列

    前言 在 jdk 的 juc 工具包中,提供了一种延迟队列 DelayQueue.延迟队列用处非常广泛,比如我们最常见的场景就是在网购或者外卖平台中发起一个订单,如果不付款,一般 15 分钟后就会被关 ...

  8. python中numpy库的一些使用

    想不用第三方库实现点深度学习的基础部分,发现numpy真的好难(笑),在此做点遇到的函数的笔记 惯例官方文档:https://docs.scipy.org/doc/numpy-1.16.1/refer ...

  9. 2019牛客多校第三场D-Big Integer

    题意 定义\(A(n)\) 为 n个1表示的十进制数,例如\(A(3) = 111\) 然后对于\(1 \le i \le n,1\le j \le m\) 问有多少的 \(pairs(i,j)\)满 ...

  10. HDU 6900 Residual Polynomial【分治 NTT】

    HDU 6900 Residual Polynomial  题意: 给出一个多项式\(f_1(x) = \sum_{i=0}^na_ix^i\) 对于任意\(i>=2\),满足\(f_i(x) ...