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 wikiMenci'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;
}

SPOJLCS Longest Common Substring的更多相关文章

  1. SPOJ-LCS Longest Common Substring 【后缀自动机】

    题目分析: 用没出现过的字符搞拼接.搞出right树,找right集合的最小和最大.如果最小和最大分居两侧可以更新答案. 代码: #include<bits/stdc++.h> using ...

  2. [SPOJ-LCS]Longest Common Substring

    题目大意: 求两个字符串的LCS. 思路: 对其中一个字符串构建SAM,然后用另一个字符串在里面匹配,按照SAM的边一直往下走,匹配到的连续的字符数就是公共字串的长度. #include<str ...

  3. Longest Common Substring II SPOJ - LCS2 (后缀自动机)

    Longest Common Substring II \[ Time Limit: 236ms\quad Memory Limit: 1572864 kB \] 题意 给出\(n\)个子串,要求这\ ...

  4. SPOJ LCS2 - Longest Common Substring II

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

  5. LintCode Longest Common Substring

    原题链接在这里:http://www.lintcode.com/en/problem/longest-common-substring/# 题目: Given two strings, find th ...

  6. Longest Common Substring

    Given two strings, find the longest common substring. Return the length of it. Example Given A = &qu ...

  7. 【SPOJ】1812. Longest Common Substring II(后缀自动机)

    http://www.spoj.com/problems/LCS2/ 发现了我原来对sam的理解的一个坑233 本题容易看出就是将所有匹配长度记录在状态上然后取min后再对所有状态取max. 但是不要 ...

  8. hdu 1403 Longest Common Substring(最长公共子字符串)(后缀数组)

    http://acm.hdu.edu.cn/showproblem.php?pid=1403 Longest Common Substring Time Limit: 8000/4000 MS (Ja ...

  9. 后缀自动机(SAM):SPOJ Longest Common Substring II

    Longest Common Substring II Time Limit: 2000ms Memory Limit: 262144KB A string is finite sequence of ...

随机推荐

  1. MySQL-5.7设置InnoDB表数据文件存储位置

    1.表空间 Innodb存储引擎可将所有数据存放于ibdata*的共享表空间,也可将每张表存放于独立的.ibd文件的独立表空间. 共享表空间以及独立表空间都是针对数据的存储方式而言的. 共享表空间: ...

  2. Boot-Repair&usb_repair

    https://help.ubuntu.com/community/Boot-Repair https://askubuntu.com/questions/500647/unable-to-mount ...

  3. hadoop源码分析

    hadoop 源代码分析(一) Google 的核心竞争技术是它的计算平台.HadoopGoogle的大牛们用了下面5篇文章,介绍了它们的计算设施. GoogleCluster:http://rese ...

  4. React Native之持久化存储(AsyncStorage、react-native-storage)的使用

    AsyncStorage是一个简单的.异步的.持久化的Key-Value存储系统,它对于App来说是全局性的.这是官网上对它的介绍.可以知道,这个asyncstorage也是以键值对的形式进行存储数据 ...

  5. javascript-实现小抽奖程序

    直接上代码 <style> *{ margin: 0; padding:0;} .prize_wrap{ width: 300px; height: 150px; } .prize_wra ...

  6. fix LayerKit framework不能提交App Store

    - 问题: - 原因 x86_64, i386是ios模拟器用的architectures.发布时,不支持这两种.但是,默认编译出来的layerkit framework支持这两种编译器 - 解决办法 ...

  7. java 处理emoji表情信息转换为String

    2种方式实现: 注意:如果发现运行时java.lang.NoClassDefFoundError:异常就是缺少了jar包.添加对应的jar包就可以. 一.emoji-java-4.0.0.jar实现 ...

  8. Pandas窗口函数

    为了处理数字数据,Pandas提供了几个变体,如滚动,展开和指数移动窗口统计的权重. 其中包括总和,均值,中位数,方差,协方差,相关性等. 下来学习如何在DataFrame对象上应用上提及的每种方法. ...

  9. virtio guest side implementation: PCI, virtio device, virtio net and virtqueue

    With the publishing of OASIS virtio specification version 1.0, virtio made another big step in becom ...

  10. JSON Web Token (JWT) 简介

    JSON Web Token (JWT) 是一种基于 token 的认证方案. JSON Web Token 的结构 一个 JWT token 看起来是这样的: eyJhbGciOiJIUzI1NiI ...