【后缀自动机】SPOJ(LCS2)[Longest Common Substring II]题解,spojlcs2
分享于 点击 22309 次 点评:24
【后缀自动机】SPOJ(LCS2)[Longest Common Substring II]题解,spojlcs2
题目概述
求n个串的最长公共子串。
解题报告
这道题是SPOJ1811的升级版,还是可以用SAM解决。
我们先对第一个字符串构造SAM,然后和SPOJ1811一样用其他串和SAM进行匹配,只不过由于有多个串,我们不能直接刷最大值来求LCS。所以我们将SAM的每个状态都添加一个min记录其他串到此状态时的最小匹配长度(当然无论如何min都不超过该状态的最大子串长度MAX)。那么答案就是所有min中的最大值吗?其实是错的!因为虽然min记录在了某个状态上,但实际上min对一切该状态的祖先都是可行的(只不过匹配的时候匹配了该状态),所以我们还需要向上传递一下,这样才能够刷出正确的答案。
这里说一下如何向上传递,其实并不需要递归处理,只需要按照MAX给所有状态排序,然后由MAX从大到小修正(儿子的MAX一定大于祖先的MAX,这个很好yy,大家可以自己想一下:P),为了不让复杂度多一个log,我们可以使用计数排序。
示例程序
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxl=100000;
int len;
char s[maxl+5];
struct SAM
{
struct node {int son[26],fa,MAX;};
node tem[2*maxl+5];int cur,ro,lst;
int newnode(int M)
{
tem[++cur].MAX=M;tem[cur].fa=0;
memset(tem[cur].son,0,sizeof(tem[cur].son));return cur;
}
void clear() {cur=0;ro=newnode(0);lst=ro;}
void Insert(char ch)
{
int ID=ch-'a';int p=lst,np=newnode(tem[p].MAX+1);
while (p&&!tem[p].son[ID]) tem[p].son[ID]=np,p=tem[p].fa;
if (!p) tem[np].fa=ro; else
{
int q=tem[p].son[ID];
if (tem[p].MAX+1==tem[q].MAX) tem[np].fa=q; else
{
int nq=newnode(tem[p].MAX+1);
memcpy(tem[nq].son,tem[q].son,sizeof(tem[q].son));
tem[nq].fa=tem[q].fa;tem[q].fa=tem[np].fa=nq;
while (p&&tem[p].son[ID]==q) tem[p].son[ID]=nq,p=tem[p].fa;
}
}
lst=np;
}
void make_SAM(char *s) {for (int i=1;s[i];i++) Insert(s[i]);}
int ha[maxl+5],who[2*maxl+5],MIN[2*maxl+5],MAX[2*maxl+5];
void Sort() //给状态排序
{
for (int i=1;i<=cur;i++) MIN[i]=tem[i].MAX; //MIN初始值
for (int i=1;i<=cur;i++) ha[tem[i].MAX]++;
for (int i=1;i<=len;i++) ha[i]+=ha[i-1];
for (int i=1;i<=cur;i++) who[ha[tem[i].MAX]--]=i;
//计数排序
}
void Update(char *s) //将s与此SAM匹配
{
int p=ro,len=0;
for (int i=1;i<=cur;i++) MAX[i]=0;
for (int i=1;s[i];i++)
{
int ID=s[i]-'a';
if (tem[p].son[ID]) p=tem[p].son[ID],len++; else
{
while (p&&!tem[p].son[ID]) p=tem[p].fa;
if (p) len=tem[p].MAX+1,p=tem[p].son[ID]; else
p=ro,len=0;
}
MAX[p]=max(MAX[p],len); //更新
}
for (int i=cur;i>=1;i--) //传递
{
int now=who[i];
MIN[now]=min(MIN[now],MAX[now]);
int fa=tem[now].fa;
if (fa) MAX[fa]=min(tem[fa].MAX,max(MAX[fa],MAX[now]));
//不要忘了和tem[fa].MAX取min,否则会出错!
}
}
int LCS() {int ans=0;for (int i=1;i<=cur;i++) ans=max(ans,MIN[i]);return ans;}
//众多MIN中取max就是LCS
};
SAM sam;
char readc()
{
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,10000,stdin);
if (l==r) return EOF; else return *l++;
}
int reads(char *s)
{
int len=0;char ch=readc();if (ch==EOF) return EOF;
while ('z'<ch||ch<'a') ch=readc();s[++len]=ch;
while ('a'<=s[len]&&s[len]<='z') s[++len]=readc();s[len--]=0;
return len;
}
int main()
{
freopen("program.in","r",stdin);
freopen("program.out","w",stdout);
len=reads(s);sam.clear();sam.make_SAM(s);sam.Sort();
while (~reads(s)) sam.Update(s);
printf("%d\n",sam.LCS());
return 0;
}
相关文章
- 暂无相关文章
用户点评