bzoj4032 [HEOI2015]最短不公共子串(SAM+序列自动机+bfs)

132 篇文章 0 订阅

序列自动机就是可以识别所有子序列的自动机,点数 O(n) ,边数 O(26n)
每个点的par就是这个点代表的字母上一次出现的位置,因此要记录last[i],表示字母i上一次出现在哪,然后每一个点的儿子i指向他后面出现的第一个字母i的位置。

求A最短的子串/子序列不是B的子串/子序列,我们两个自动机一起跑,状态[x][y]表示一个字符串匹配到了A自动机的x节点,B自动机的y节点,因为是bfs,可以发现此时的字符串是符合状态的最短的。如果A匹配上了,而B没匹配上,就是答案了。

复杂度 O(26n2)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <utility>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 2010
#define pa pair<pair<int,int>,int>
int m;
char s[N];bool vis[N<<1][N<<1];
struct SAM{
    int n,son[N<<1][26],par[N<<1],mx[N<<1],rt,last;
    inline void init(){n=0;last=rt=++n;}
    inline void ins(int ch){
        int p=last,np=++n;mx[np]=mx[p]+1;last=np;
        for(;p&&!son[p][ch];p=par[p]) son[p][ch]=np;
        if(!p){par[np]=rt;return;}
        int q=son[p][ch];
        if(mx[q]==mx[p]+1){par[np]=q;return;}
        int nq=++n;mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));
        par[nq]=par[q];par[q]=par[np]=nq;
        for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
    }
}A,B;
struct SEAM{
    int n,son[N][26],par[N],last[26],rt;
    inline void init(){n=0;rt=++n;for(int i=0;i<26;++i) last[i]=rt;}
    inline void ins(int ch){
        int np=++n;
        for(int i=0;i<26;++i)
            for(int p=last[i];p&&!son[p][ch];p=par[p]) son[p][ch]=np;
        par[np]=last[ch];last[ch]=np;
    }
}AE,BE;
inline int bfs1(){
    queue<pa>q;memset(vis,0,sizeof(vis));
    q.push(make_pair(make_pair(A.rt,B.rt),0));vis[A.rt][B.rt]=1;
    while(!q.empty()){
        int x=q.front().first.first,y=q.front().first.second,z=q.front().second;q.pop();
        for(int i=0;i<26;++i){
            int xx=A.son[x][i],yy=B.son[y][i];
            if(!xx) continue;if(!yy) return z+1;
            if(vis[xx][yy]) continue;vis[xx][yy]=1;
            q.push(make_pair(make_pair(xx,yy),z+1));
        }
    }return -1;
}
inline int bfs2(){
    queue<pa>q;memset(vis,0,sizeof(vis));
    q.push(make_pair(make_pair(A.rt,BE.rt),0));vis[A.rt][BE.rt]=1;
    while(!q.empty()){
        int x=q.front().first.first,y=q.front().first.second,z=q.front().second;q.pop();
        for(int i=0;i<26;++i){
            int xx=A.son[x][i],yy=BE.son[y][i];
            if(!xx) continue;if(!yy) return z+1;
            if(vis[xx][yy]) continue;vis[xx][yy]=1;
            q.push(make_pair(make_pair(xx,yy),z+1));
        }
    }return -1;
}
inline int bfs3(){
    queue<pa>q;memset(vis,0,sizeof(vis));
    q.push(make_pair(make_pair(AE.rt,B.rt),0));vis[AE.rt][B.rt]=1;
    while(!q.empty()){
        int x=q.front().first.first,y=q.front().first.second,z=q.front().second;q.pop();
        for(int i=0;i<26;++i){
            int xx=AE.son[x][i],yy=B.son[y][i];
            if(!xx) continue;if(!yy) return z+1;
            if(vis[xx][yy]) continue;vis[xx][yy]=1;
            q.push(make_pair(make_pair(xx,yy),z+1));
        }
    }return -1;
}
inline int bfs4(){
    queue<pa>q;memset(vis,0,sizeof(vis));
    q.push(make_pair(make_pair(AE.rt,BE.rt),0));vis[AE.rt][BE.rt]=1;
    while(!q.empty()){
        int x=q.front().first.first,y=q.front().first.second,z=q.front().second;q.pop();
        for(int i=0;i<26;++i){
            int xx=AE.son[x][i],yy=BE.son[y][i];
            if(!xx) continue;if(!yy) return z+1;
            if(vis[xx][yy]) continue;vis[xx][yy]=1;
            q.push(make_pair(make_pair(xx,yy),z+1));
        }
    }return -1;
}
int main(){
//  freopen("a.in","r",stdin);
    scanf("%s",s+1);m=strlen(s+1);A.init();AE.init();
    for(int i=1;i<=m;++i) A.ins(s[i]-'a'),AE.ins(s[i]-'a');
    scanf("%s",s+1);m=strlen(s+1);B.init();BE.init();
    for(int i=1;i<=m;++i) B.ins(s[i]-'a'),BE.ins(s[i]-'a');
    printf("%d\n",bfs1());printf("%d\n",bfs2());
    printf("%d\n",bfs3());printf("%d\n",bfs4());
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值