【spoj1811 & spoj1812 - LCS1 & LCS2】sam

【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;
}

 

posted @ 2016-09-03 08:48 拦路雨偏似雪花 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值