SPOJ LCS Longest Common Substring 和 LG3804 【模板】后缀自动机
Longest Common Substring
给两个串A和B,求这两个串的最长公共子串。
no more than 250000
分析
参照OI wiki。
给定两个字符串 S 和 T ,求出最长公共子串,公共子串定义为在 S 和 T 中 都作为子串出现过的字符串 X 。
我们为字符串 S 构造后缀自动机。
我们现在处理字符串 T ,对于每一个前缀都在 S 中寻找这个前缀的最长后缀。换句话 说,对于每个字符串 T 中的位置,我们想要找到这个位置结束的 S 和 T 的最长公 共子串的长度。
为了达到这一目的,我们使用两个变量,当前状态 v 和 当前长度 l 。这两 个变量描述当前匹配的部分:它的长度和它们对应的状态。
一开始 v=t_0且 l=0 ,即,匹配为空串。
现在我们来描述如何添加一个字符 T[i] 并为其重新计算答案:
如果存在一个从 v 到字符 T[i] 的转移,我们只需要转移并让 l 自增一。
如果不存在这样的转移,我们需要缩短当前匹配的部分,这意味着我们需要按照以下后 缀链接进行转移:
v=link(v)
与此同时,需要缩短当前长度。显然我们需要将 l 赋值为 len(v) ,因为经过这个后缀链接后我们到达的状态所对应的最长字符串是一个子串。
如果仍然没有使用这一字符的转移,我们继续重复经过后缀链接并减小 l ,直到我们 找到一个转移或到达虚拟状态 -1 (这意味着字符 T[i] 根本没有在 S 中出现过, 所以我们设置 v=l=0 )。
问题的答案就是所有 l 的最大值。
这一部分的时间复杂度为 O(length(T)) ,因为每次移动我们要么可以使 l 增加一, 要么可以在后缀链接间移动几次,每次都减小 l 的值。
时间复杂度\(O(|S| + |T|)\)。
co int N=5e5;
namespace SAM
{
int tot,last;
int ch[N][26],fail[N]={-1},len[N];
void extend(int k)
{
int cur=++tot;
len[cur]=len[last]+1;
int p=last;
while(~p&&!ch[p][k])
{
ch[p][k]=cur;
p=fail[p];
}
if(p==-1)
fail[cur]=0;
else
{
int q=ch[p][k];
if(len[q]==len[p]+1)
fail[cur]=q;
else
{
int clone=++tot;
std::copy(ch[q],ch[q]+26,ch[clone]);
fail[clone]=fail[q],len[clone]=len[p]+1;
while(~p&&ch[p][k]==q)
{
ch[p][k]=clone;
p=fail[p];
}
fail[cur]=fail[q]=clone;
}
}
last=cur;
}
void ins(char s[],int n)
{
for(int i=0;i<n;++i)
extend(s[i]-'a');
}
void solve(char s[],int n)
{
int ans=0,v=0,l=0;
for(int i=0;i<n;++i)
{
int k=s[i]-'a';
if(ch[v][k])
v=ch[v][k],++l;
else
{
while(~v&&!ch[v][k])
v=fail[v];
if(v==-1)
v=l=0;
else
l=len[v]+1,v=ch[v][k];
}
ans=std::max(ans,l);
}
printf("%d\n",ans);
}
}
char buf[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
scanf("%s",buf);
SAM::ins(buf,strlen(buf));
scanf("%s",buf);
SAM::solve(buf,strlen(buf));
return 0;
}
【模板】后缀自动机
给定一个只包含小写字母的字符串S,
请你求出 S 的所有出现次数不为 1 的子串的出现次数乘上该子串长度的最大值。
对于100%的数据,|S|<=106
分析
学习资料:OI wiki,Menci's Blog,张天杨《后缀自动机及其应用》。
证明后缀自动机的状态数和转移数是线性的:
不同的状态的Right集只能是不相交或者真包含关系。
这种性质很容易让人想到树对不对,按照真包含关系做成树的话,大于一个元素的集合必定有大于等于两个儿子(保证了不会出现无用的链),只有一个元素的n个集合就是叶节点,所以总节点数小于等于\(2n-1\)。这样就说明状态数是线性的了。还差一点点,那就是转移数也是线性的。下面就来证明:
取自动机的一个有向的生成树,使得初始状态出发可以到达所有状态。对于一条不在树上的边,我们可以在树上从初始状态走到它的起点,然后经过它并走到终态,走的这条路径对应的一定是原串的一个后缀(初态→终态,后缀自动机)。所以每条非树边一定可以对应某些后缀,而不同的两条非树边对应的后缀不可能有重复(若重复,跑该后缀的时候状态转移不唯一),所以非树边数小于等于\(n-1\)。算上生成树上的边,我们得到:转移数小于等于\((2n-1-1)+(n-1)=3n-3\)。
我们令叶子节点的size=1.暴力建出parent树然后dfs,求出每个节点的right集合size,然后求len×size的最大值就行了。
时间复杂度:\(O(|S|)\)
co int N=2e6;
namespace SAM
{
int tot,last;
int ch[N][26],fail[N],len[N],siz[N];
void init()
{
tot=last=0;
fail[0]=-1,len[0]=0;
}
void extend(int k)
{
int cur=++tot;
len[cur]=len[last]+1,siz[cur]=1;
int p=last;
while(~p&&!ch[p][k])
{
ch[p][k]=cur;
p=fail[p];
}
if(p==-1)
fail[cur]=0;
else
{
int q=ch[p][k];
if(len[p]+1==len[q])
fail[cur]=q;
else
{
int clone=++tot;
std::copy(ch[q],ch[q]+26,ch[clone]);
fail[clone]=fail[q],len[clone]=len[p]+1;
while(~p&&ch[p][k]==q)
{
ch[p][k]=clone;
p=fail[p];
}
fail[q]=fail[cur]=clone;
}
}
last=cur;
}
void ins(char*s,int n)
{
for(int i=0;i<n;++i)
extend(s[i]-'a');
}
int nx[N],to[N];
ll ans;
void build()
{
for(int i=1;i<=tot;++i)
nx[i]=to[fail[i]],to[fail[i]]=i;
}
void dfs(int x)
{
for(int i=to[x];i;i=nx[i])
{
dfs(i);
siz[x]+=siz[i];
}
if(siz[x]>1)
ans=std::max(ans,(ll)siz[x]*len[x]);
}
void solve()
{
build();
dfs(0);
printf("%lld\n",ans);
}
}
char buf[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
SAM::init();
scanf("%s",buf);
SAM::ins(buf,strlen(buf));
SAM::solve();
return 0;
}
SPOJ LCS Longest Common Substring 和 LG3804 【模板】后缀自动机的更多相关文章
- 后缀自动机(SAM) :SPOJ LCS - Longest Common Substring
LCS - Longest Common Substring no tags A string is finite sequence of characters over a non-empty f ...
- LCS2 - Longest Common Substring II(spoj1812)(sam(后缀自动机)+多串LCS)
A string is finite sequence of characters over a non-empty finite set \(\sum\). In this problem, \(\ ...
- SPOJ LCS - Longest Common Substring 字符串 SAM
原文链接http://www.cnblogs.com/zhouzhendong/p/8982392.html 题目传送门 - SPOJ LCS 题意 求两个字符串的最长公共连续子串长度. 字符串长$\ ...
- SPOJ LCS Longest Common Substring(后缀自动机)题解
题意: 求两个串的最大\(LCS\). 思路: 把第一个串建后缀自动机,第二个串跑后缀自动机,如果一个节点失配了,那么往父节点跑,期间更新答案即可. 代码: #include<set> # ...
- spoj 1811 LCS - Longest Common Substring (后缀自己主动机)
spoj 1811 LCS - Longest Common Substring 题意: 给出两个串S, T, 求最长公共子串. 限制: |S|, |T| <= 1e5 思路: dp O(n^2 ...
- spoj1811 LCS - Longest Common Substring
地址:http://www.spoj.com/problems/LCS/ 题面: LCS - Longest Common Substring no tags A string is finite ...
- 【SPOJ】Longest Common Substring II (后缀自动机)
[SPOJ]Longest Common Substring II (后缀自动机) 题面 Vjudge 题意:求若干个串的最长公共子串 题解 对于某一个串构建\(SAM\) 每个串依次进行匹配 同时记 ...
- 【SPOJ】Longest Common Substring(后缀自动机)
[SPOJ]Longest Common Substring(后缀自动机) 题面 Vjudge 题意:求两个串的最长公共子串 题解 \(SA\)的做法很简单 不再赘述 对于一个串构建\(SAM\) 另 ...
- 【SP1811】LCS - Longest Common Substring
[SP1811]LCS - Longest Common Substring 题面 洛谷 题解 建好后缀自动机后从初始状态沿着现在的边匹配, 如果失配则跳它的后缀链接,因为你跳后缀链接到达的\(End ...
随机推荐
- beyond Compare 30天过期后的处理办法
打开Beyond Compare 4,提示已经超出30天试用期限制,解决方法: 修改C:\Program Files\Beyond Compare 4\BCUnrar.dll,这个文件重命名或者直接删 ...
- jdk 7&8 new features
7 Diamond Operator(菱形操作符) You can omitted the type declaration of the right when working with Generi ...
- 微信自研生产级paxos类库PhxPaxos实现原理介绍
转载自: http://mp.weixin.qq.com/s?__biz=MzI4NDMyNTU2Mw==&mid=2247483695&idx=1&sn=91ea4229 ...
- [转帖]Red Hat K8s 关键人物 Grant Shipley 跳槽到 VMware
Red Hat K8s 关键人物 Grant Shipley 跳槽到 VMware https://news.cnblogs.com/n/641944/ 这四小时的工作效率 太无敌了.. 投递人 ...
- Keras中图像维度介绍
报错问题: ValueError: Negative dimension size caused by subtracting 5 from 1 for 'conv2d_1/convolution' ...
- 【HC89S003F4开发板】9ASM写定时器1
HC89S003F4开发板ASM写定时器1 一.实现过程 1.外部寄存器设置 扩展 XSFR 采用和 XRAM 同样的访问方式,使用 MOVX A, @DPTR 和 MOVX @DPTR ,A 来进行 ...
- bootstrap栅格系统的container和row一些关系
container有个15px的padding,而我们设定的每个col也都有15px的padding,如果两者直接配合,那么就会产生30px的间距,导致内容和浏览器边框的距离较大,所以用row将所有的 ...
- Broom |tidy up a bit,模型,检验结果一键输出!
本文首发于“生信补给站”公众号,https://mp.weixin.qq.com/s/TqFk66F2gUu_k8WEjKbLtA 更多关于R语言,ggplot2绘图,生信分析的内容,敬请关注小号. ...
- Math.random()的加密安全替换方法window.crypto.getRandomValues
Math.random() 返回介于 0(包含) ~ 1(不包含) 之间的一个随机数. Math.random()函数不是加密安全的随机数生成器. window.crypto.getRandomVal ...
- 爬虫相关--requests库
requests的理想:HTTP for Humans 一.八个方法 相比较urllib模块,requests模块要简单很多,但是需要单独安装: 在windows系统下只需要在命令行输入命令 pip ...