zoukankan      html  css  js  c++  java
  • [HEOI2015]最短不公共子串

    四合一的题。

    简单粗暴的方法:

    子串匹配——SAM

    子序列匹配——序列自动机

    关于序列自动机:序列自动机—— [FJOI2016]所有公共子序列问题

    (其实这个玩意没有什么,n+1个点,每个点的字符集的每条出边连向其后的第一个字符,这样保证尽可能用靠前的,后面的能凑出的子序列就能更多,1号点是rt)

    类似于SAM,序列自动机的路径条数就是子序列个数。

    这样的话,对AB两个串各建造一个SAM和序列自动机

    四问就分别在两个机子上bfs跑

    如果A有的出边,而B没有,那么返回深度作为答案。

    否则A,B都有,入队。

    正确性:bfs按深度,会把深度为d的所有公共的子串/子序列搜完后再搜下一个。第一个合法的就是最短的。

    复杂度:记忆化一下,因为到了同样一个数对(Ax,By)时候,后面的路径都是固定的了。之前没有找到过,后面也不会找到。直接continue

    这样,复杂度就是状态数O(n^2)

    就是考察对自动机"路径与子串一一对应"的理解。

    #include<bits/stdc++.h>
    #define il inline
    #define reg register int
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=2002;
    char a[N],b[N];
    int l1,l2;
    struct SAM{
        int nd,cnt,fa[2*N],ch[2*N][26];
        int len[2*N];
        SAM(){
            nd=cnt=1;
        }
        void ins(int c,int pos){
            //cout<<c<<" "<<pos<<" "<<cnt<<endl;
            int p=nd;nd=++cnt;
            len[nd]=pos;
            for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=nd;
            if(p==0) {fa[nd]=1;return;}
            int q=ch[p][c];
            if(len[q]==len[p]+1){fa[nd]=q;return;}
            len[++cnt]=len[p]+1;
            for(reg i=0;i<=25;++i) ch[cnt][i]=ch[q][i];
            fa[cnt]=fa[q];fa[q]=cnt;fa[nd]=cnt;
            for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=cnt;
        }
    }SA,SB;
    struct XU{
        int cnt,ch[N][26];
        int las[26];
        void build(char *s,int len){
            for(reg i=len;i>=0;--i){
                for(reg j=0;j<=25;++j){
                    if(las[j]) {
                        ch[i+1][j]=las[j]; 
                        //cout<<" to "<<i+1<<" "<<j<<" "<<las[j]<<endl;
                    }
                }
                if(i)las[s[i]-'a']=i+1;
            }
        }
    }XA,XB;
    struct po{
        int x,y,d;
        po(){}
        po(int xx,int yy,int dd){
            x=xx,y=yy,d=dd;
        }
    };
    queue<po>q;
    int vis[2*N][2*N];
    int bfs1(){
        while(!q.empty()) q.pop();
        q.push(po(1,1,0));
        vis[1][1]=1;
        while(!q.empty()){
            po now=q.front();q.pop();
            for(reg i=0;i<=25;++i){
                if(vis[SA.ch[now.x][i]][SB.ch[now.y][i]]==1) continue;
                vis[SA.ch[now.x][i]][SB.ch[now.y][i]]=1;
                if(SA.ch[now.x][i]&&!SB.ch[now.y][i]) return now.d+1;
                if(SA.ch[now.x][i]) 
                {    
                    q.push(po(SA.ch[now.x][i],SB.ch[now.y][i],now.d+1));
                }
            }
        }
        return -1;
    }
    int bfs2(){
        while(!q.empty()) q.pop();
        q.push(po(1,1,0));
        vis[1][1]=2;
        while(!q.empty()){
            po now=q.front();q.pop();
            for(reg i=0;i<=25;++i){
                if(vis[SA.ch[now.x][i]][XB.ch[now.y][i]]==2) continue;
                vis[SA.ch[now.x][i]][XB.ch[now.y][i]]=2;
                if(SA.ch[now.x][i]&&!XB.ch[now.y][i]) return now.d+1;
                if(SA.ch[now.x][i]) 
                {    
                    q.push(po(SA.ch[now.x][i],XB.ch[now.y][i],now.d+1));
                }
            }
        }
        return -1;
    }
    int bfs3(){
        while(!q.empty()) q.pop();
        q.push(po(1,1,0));
        vis[1][1]=3;
        while(!q.empty()){
            po now=q.front();q.pop();
            for(reg i=0;i<=25;++i){
                if(vis[XA.ch[now.x][i]][SB.ch[now.y][i]]==3) continue;
                vis[XA.ch[now.x][i]][SB.ch[now.y][i]]=3;
                if(XA.ch[now.x][i]&&!SB.ch[now.y][i]) return now.d+1;
                if(XA.ch[now.x][i]) 
                {    
                    q.push(po(XA.ch[now.x][i],SB.ch[now.y][i],now.d+1));
                }
            }
        }
        return -1;
    }
    int bfs4(){
        while(!q.empty()) q.pop();
        q.push(po(1,1,0));
        vis[1][1]=4;
        while(!q.empty()){
            po now=q.front();q.pop();
            //cout<<now.x<<" || "<<now.y<<" dd "<<now.d<<endl;
            for(reg i=0;i<=25;++i){
                if(vis[XA.ch[now.x][i]][XB.ch[now.y][i]]==4) continue;
                vis[XA.ch[now.x][i]][XB.ch[now.y][i]]=4;
                if(XA.ch[now.x][i]&&!XB.ch[now.y][i]) return now.d+1;
                if(XA.ch[now.x][i]) 
                {    
                    //cout<<" goto "<<XA.ch[now.x][i]<<" "<<
                    q.push(po(XA.ch[now.x][i],XB.ch[now.y][i],now.d+1));
                }
            }
        }
        return -1;
    }
    int main(){
        scanf("%s",a+1);
        scanf("%s",b+1);
        l1=strlen(a+1);l2=strlen(b+1);
        
        for(reg i=1;i<=l1;++i) SA.ins(a[i]-'a',i);
        for(reg i=1;i<=l2;++i) SB.ins(b[i]-'a',i);
        //cout<<SA.cnt<<" "<<SB.cnt<<endl;
        XA.build(a,l1);XB.build(b,l2);
        printf("%d
    %d
    %d
    %d",bfs1(),bfs2(),bfs3(),bfs4());
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/12 9:10:40
    */

    考虑到,我们要最小化一个串的长度,使得这个串在B的机子上跑完回到NULL节点。

    所以,也可以DP做。f[i][j]表示,考虑A中前i个位置,匹配到B的机子上的j位置,最小长度。直接在所有机子位置后尝试匹配i+1位,取min即可完成转移。

    答案就是f[lenA][NULL]

  • 相关阅读:
    POJ 2195 Going Home(费用流)
    HDU 2485 Destroying the bus stations(费用流)
    POJ 3281 Dining(最大流)
    POJ 1122 FDNY to the Rescue!(最短路+路径输出)
    HDU 4747 Mex(线段树)
    POJ 2337 Catenyms
    UVa 10328 Coin Toss(Java大数+递推)
    HDU 1811 Rank of Tetris(拓扑排序+并查集)
    ZOJ 3747 Attack on Titans
    UVa 11404 回文子序列(LCS求最长回文串长度)
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10109332.html
Copyright © 2011-2022 走看看