【spoj1811 & spoj1812 - LCS1 & LCS2】sam
spoj1811 给两个长度小于100000的字符串 A 和 B,求出他们的最长公共连续子串。
先将串 A 构造为 SAM ,然后用 B 按如下规则去跑自动机。
用一个变量 lcs 记录当前的最长公共子串,初始化为0。
设当前状态结点为 p,要匹配的字符为 c,若 go[c] 中有边,说明能够转移状态,则转移并 lcs++;
若不能转移则将状态移动到 p 的 par ,如果仍然不能转移则重复该过程直到 p 回到根节点,并将 lcs 置为 0;
如果在上一个过程中进入了能够转移的状态,则设 lcs 为当前状态的 val。
为什么失配后要移向 par 呢?因为在状态 p 上失配说明该状态的 [min,max] 所表示字符串都不是 B 中的子串,但是比它们短的后缀仍有可能是 B 的子串,而 par 指针恰好指向了该状态的后缀。
spoj1812 要求多个串的最长公共子串,n<=100000,10个串。
本来想用广义sam,然后对每个点位压10位表示它能到哪些串,最后dfs一遍。
20*10^5=10^6个点,时间压得很紧直接超时。
参照上题做法,就把一个串A放进去,nlcs[x]维护当前串与A在自动机的x节点匹配的最大长度,lcs[x]维护前i个串。做完一个串后for一遍,x没有走到过的点lcs[x]=0,其余的lcs[x]=minn(lcs[x],nlcs[x]);
spoj1811
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int N=2*100010;
char a[N],b[N];
int al,bl,tot,last,son[N][30],step[N],pre[N];
int maxx(int x,int y){return x>y ? x:y;}
int add_node(int x)
{
step[++tot]=x;
return tot;
}
void extend(int ch)
{
int p=last,np=add_node(step[p]+1);
while(p && !son[p][ch]) son[p][ch]=np,p=pre[p];
if(!p) pre[np]=1;
else
{
int q=son[p][ch];
if(step[q]==step[p]+1) pre[np]=q;
else
{
int nq=add_node(step[p]+1);
memcpy(son[nq],son[q],sizeof(son[q]));
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
while(son[p][ch]==q) son[p][ch]=nq,p=pre[p];
}
}
last=np;
}
int main()
{
freopen("a.in","r",stdin);
// freopen("me.out","w",stdout);
memset(son,0,sizeof(son));
memset(pre,0,sizeof(pre));
memset(step,0,sizeof(step));
tot=0;add_node(0);last=1;
scanf("%s%s",a+1,b+1);
al=strlen(a+1),bl=strlen(b+1);
for(int i=1;i<=al;i++) extend(a[i]-'a'+1);
int ch,x=1,ans=0,len=0;
for(int i=1;i<=bl;i++)
{
ch=b[i]-'a'+1;
while(x && !son[x][ch]) x=pre[x],len=step[x];
if(x==0) x=1;
if(son[x][ch]) x=son[x][ch],len++;
ans=maxx(ans,len);
}
printf("%d\n",ans);
return 0;
}
spoj1812
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int N=2*100010;
char s[15][N];
int n,tot,last,son[N][30],step[N],pre[N],lcs[N],nlcs[N],l[15];
bool vis[N];
struct node{
int x,y,next;
}a[2*N];
int maxx(int x,int y){return x>y ? x:y;}
int minn(int x,int y){return x<y ? x:y;}
int add_node(int x)
{
step[++tot]=x;lcs[tot]=x;
return tot;
}
void extend(int ch)
{
int p=last,np=add_node(step[p]+1);
while(p && !son[p][ch]) son[p][ch]=np,p=pre[p];
if(!p) pre[np]=1;
else
{
int q=son[p][ch];
if(step[q]==step[p]+1) pre[np]=q;
else
{
int nq=add_node(step[p]+1);
memcpy(son[nq],son[q],sizeof(son[q]));
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
while(son[p][ch]==q) son[p][ch]=nq,p=pre[p];
}
}
last=np;
}
void solve(int id)
{
memset(nlcs,0,sizeof(nlcs));
memset(vis,0,sizeof(vis));
int x=1,len=0,ch;
for(int i=1;i<=l[id];i++)
{
ch=s[id][i]-'a'+1;
while(x && !son[x][ch])
{
x=pre[x],len=step[x],vis[x]=1;
nlcs[x]=maxx(nlcs[x],len);
}
if(x==0) x=1,vis[1]=1;
if(son[x][ch]) x=son[x][ch],vis[x]=1,len++;
nlcs[x]=maxx(nlcs[x],len);
}
for(int i=1;i<=tot;i++)
if(!vis[i]) lcs[i]=0;
else lcs[i]=minn(lcs[i],nlcs[i]);
}
int main()
{
freopen("a.in","r",stdin);
// freopen("me.out","w",stdout);
memset(son,0,sizeof(son));
memset(pre,0,sizeof(pre));
memset(step,0,sizeof(step));
tot=0;add_node(0);last=1;n=0;
while(scanf("%s",s[++n]+1)!=EOF)
{
l[n]=strlen(s[n]+1);
}n--;
for(int i=1;i<=l[1];i++) extend(s[1][i]-'a'+1);
for(int i=2;i<=n;i++) solve(i);
int ans=0;
for(int i=1;i<=tot;i++) ans=maxx(ans,lcs[i]);
printf("%d\n",ans);
return 0;
}