【BZOJ4032】【HEOI2015】最短不公共子串 后缀自动机

这篇博客详细介绍了如何利用后缀自动机解决BZOJ4032和HEOI2015比赛中的最短不公共子串问题。通过T1到T4四个部分,逐步解析了不同方法的时间复杂度和优化策略,包括按长度 bfs、记录特定位置的字母出现位置以及利用去重技巧。最后提到了代码实现时需要注意的常数优化问题。
摘要由CSDN通过智能技术生成

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/45313429");
}

题解:

T1:

我们按长度bfs所有的串,对于每个串记录A串中终点位置、B串中终点位置(B串中位置由B的后缀自动机中节点标号表示)、长度—— (x,y,l)
然后 (x,y,l) 可以 O(1) 转移到 (x+1,son[y,stringax+1a],l+1)
时间复杂度 O(n2)

T2:

我们记录 prex,i 表示 位置 x 之后第一个字母 i 的位置。
然后 (x,y,l) 可以 O(1) 转移到 (x+1,pre[y,stringax+1a],l+1)
时间复杂度 O(n2)

T3:

我们发现对于有相同 (x,y) (x,y,l) 只取一个 l 最小的就好啦,因为对于 l 不同的 (x,y) ,其后需要的不公共子串的最短达成 Δl 是相同的!
枚举子序列的方法是每个节点都可以枚举它后面还有哪些字母,
然后 (x,y,l) 可以 O(1) 转移到 (pre[x,?],son[y,?],l+1)
所以可以判断去重。
时间复杂度 O(n2) 26 的常数。

T4:

把前面的略结合一下就好啦~
然后 (x,y,l) 可以 O(1) 转移到 (pre[x,?],preB[y,?],l+1)
时间复杂度 O(n2) 26 的常数。

-

另外前两问也可以用判断去重的方法。
另外要注意去重的这个东西需要开 [N][N<<1]

代码:

真是悲伤的故事……
我优化了一下我的常数,结果它慢了将近一半……
400ms 552ms

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2020
#define T 26
using namespace std;
struct SAM
{
    int son[N<<1][T],dep[N<<1],pa[N<<1],last,cnt;
    void init(){last=cnt=1;}
    int newnode(int p){dep[++cnt]=dep[p]+1;return cnt;}
    void add(int x)
    {
        int p=last;
        int np=newnode(p);
        while(p&&son[p][x]==0)son[p][x]=np,p=pa[p];
        if(!p)pa[np]=1;
        else {
            int q=son[p][x];
            if(dep[q]==dep[p]+1)pa[np]=q;
            else {
                int nq=newnode(p);
                pa[nq]=pa[q],pa[q]=pa[np]=nq;
                memcpy(son[nq],son[q],sizeof son[q]);
                while(p&&son[p][x]==q)son[p][x]=nq,p=pa[p];
            }
        }
        last=np;
    }
    void print()
    {
        for(int i=1;i<=30;i++)
        {
            for(int j=0;j<T;j++)if(son[i][j])
                printf("%d %d\n",i,son[i][j]);
        }
    }
}sam;
char s[N],t[N];
int pre1[N][T],pre2[N][T],n,m;
struct Eli
{
    int x,y,z;
    Eli(int _x=0,int _y=0,int _z=0):x(_x),y(_y),z(_z){}
};
int vis[N][N<<1];
queue<Eli>q;
int getans(int f)
{
    int i,x,y;
    Eli U;
    while(!q.empty())q.pop();
    if(f<3)
    {
        U.z=0;
        if(f==1)U.y=1;
        else U.y=0;
        for(i=0;i<n;i++)
        {
            U.x=i;
            q.push(U);
        }
        while(!q.empty())
        {
            U=q.front(),q.pop();
            x=U.x+1,y=s[x]-'a';
            if(x>n)continue;
            if(f==1)y=sam.son[U.y][y];
            else y=pre2[U.y][y];

            if(!y)return U.z+1;
            else if(vis[x][y]!=f)
            {
                vis[x][y]=f;
                q.push(Eli(x,y,U.z+1));
            }
        }
    }
    else if(f==3)
    {
        q.push(Eli(0,1,0));
        while(!q.empty())
        {
            U=q.front(),q.pop();
            for(i=0;i<T;i++)if(x=pre1[U.x][i])
            {
                y=sam.son[U.y][i];
                if(!y)return U.z+1;
                else if(vis[x][y]!=f)
                {
                    vis[x][y]=f;
                    q.push(Eli(x,y,U.z+1));
                }
            }
        }
    }
    else {
        q.push(Eli(0,0,0));
        while(!q.empty())
        {
            U=q.front(),q.pop();
            for(i=0;i<T;i++)if(x=pre1[U.x][i])
            {
                y=pre2[U.y][i];
                if(!y)return U.z+1;
                else if(vis[x][y]!=f)
                {
                    vis[x][y]=f;
                    q.push(Eli(x,y,U.z+1));
                }
            }
        }
    }
    return -1;
}
int main()
{
    int i,j,k;
    scanf("%s%s",s+1,t+1);
    n=strlen(s+1),m=strlen(t+1);
    sam.init();
    for(i=1;i<=m;i++)sam.add(t[i]-'a');
    for(i=n-1;~i;i--)
    {
        memcpy(pre1[i],pre1[i+1],sizeof pre1[i+1]);
        pre1[i][s[i+1]-'a']=i+1;
    }
    for(i=m-1;~i;i--)
    {
        memcpy(pre2[i],pre2[i+1],sizeof pre2[i+1]);
        pre2[i][t[i+1]-'a']=i+1;
    }
    printf("%d\n%d\n%d\n%d\n",getans(1),getans(2),getans(3),getans(4));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值