1. 程式人生 > >BZOJ4032[HEOI2015]最短不公共子串——序列自動機+後綴自動機+DP+貪心

BZOJ4032[HEOI2015]最短不公共子串——序列自動機+後綴自動機+DP+貪心

貪心 [1] spa 字母 描述 pan int nbsp urn

題目描述

在虐各種最長公共子串、子序列的題虐的不耐煩了之後,你決定反其道而行之。

一個串的“子串”指的是它的連續的一段,例如bcd是abcdef的子串,但bde不是。 一個串的“子序列”指的是它的可以不連續的一段,例如bde是abcdef的子串,但bdd不是。 下面,給兩個小寫字母串A,B,請你計算: (1) A的一個最短的子串,它不是B的子串 (2) A的一個最短的子串,它不是B的子序列 (3) A的一個最短的子序列,它不是B的子串 (4) A的一個最短的子序列,它不是B的子序列

輸入

有兩行,每行一個小寫字母組成的字符串,分別代表A和B。

輸出

輸出4行,每行一個整數,表示以上4個問題的答案的長度。如果沒有符合要求的答案,輸出-1.

樣例輸入

aabbcc
abcabc

樣例輸出

2
4
2
4

提示

對於100%的數據,A和B的長度都不超過2000

真正的四合一,一題更比四題強。

本題需要用到序列自動機和後綴自動機,後綴自動機在這裏就不贅述了,說一下序列自動機:序列自動機就是對於序列的每一位$i$維護$next[i][j]$表示在第$i$個數之後最早出現$j$數字的位置,構建時只需要倒序枚舉序列更新$next$數組即可。設串長為$n$。

子任務1

維護$f[i][j]$表示以$A$的第$i$個字符為結尾的前綴和以$B$的第$j$個字符為結尾的前綴的最長公共後綴,那麽對於每個$i$,枚舉所有的$j$並取$f[i][j]$的最大值$+1$來更新答案。註意當$f[i][j]$的最大值等於$i$時不能更新答案。時間復雜度為$O(n^2)$

子任務2

對$B$建序列自動機,對於以$A$的第$i$個字符為開頭的後綴,我們將它在序列自動機上匹配,當到一個位置失配時,用當前匹配長度$+1$來更新答案。時間復雜度為$O(n^2)$。

子任務3

對$B$建後綴自動機,維護$f[i]$表示$A$的子序列匹配到後綴自動機上的$i$點的最短長度。枚舉$A$的每個字符來更新$f$數組:當自動機上當前點$x$能匹配當前枚舉字符時$f[to]=min(f[to],f[x]+1)$,否則用$f[x]+1$來更新答案。註意$x$要倒序枚舉防止更新到當前層。時間復雜度為$O(n^2)$。

子任務4

與子任務3的做法類似,只需要將後綴自動機換成序列自動機即可。時間復雜度為$O(n^2)$。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
char S[3000];
char T[3000];
namespace subtask1
{
    int f[3000][3000];
    int solve()
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(S[i]==T[j])
                {
                    f[i][j]=f[i-1][j-1]+1;
                }
            }
        }
        int ans=1<<30;
        for(int i=1;i<=n;i++)
        {
            int res=0;
            for(int j=1;j<=m;j++)
            {
                res=max(res,f[i][j]);
            }
            if(res!=i)
            {
                ans=min(res+1,ans);
            }
        }
        return ans>n?-1:ans;
    }
};
namespace subtask2
{
    int next[3000][30];
    int suf[30];
    int solve()
    {
        for(int i=0;i<26;i++)
        {
            suf[i]=m+1;
        }
        for(int i=m;i>=0;i--)
        {
            for(int j=0;j<26;j++)
            {
                next[i][j]=suf[j];
            }
            suf[T[i]-‘a‘]=i;
        }
        int ans=1<<30;
        for(int i=1;i<=n;i++)
        {
            int now=0;
            for(int j=i;j<=n;j++)
            {
                now=next[now][S[j]-‘a‘];
                if(now>m)
                {
                    ans=min(ans,j-i+1);
                    break;
                }
            }
        }
        return ans>n?-1:ans;
    }
};
namespace subtask3
{
    int tr[5000][30];
    int len[5000];
    int pre[5000];
    int f[5000];
    int cnt=1;
    int last=1;
    void insert(int x)
    {
        int p=last;
        int np=++cnt;
        last=np;
        len[np]=len[p]+1;
        for(;p&&!tr[p][x];p=pre[p])
        {
            tr[p][x]=np;
        }
        if(!p)
        {
            pre[np]=1;
        }
        else
        {
            int q=tr[p][x];
            if(len[p]+1==len[q])
            {
                pre[np]=q;
            }
            else
            {
                int nq=++cnt;
                pre[nq]=pre[q];
                memcpy(tr[nq],tr[q],sizeof(tr[q]));
                pre[np]=pre[q]=nq;
                len[nq]=len[p]+1;
                for(;p&&tr[p][x]==q;p=pre[p])
                {
                    tr[p][x]=nq;
                }
            }
        }
    }
    int solve()
    {
        for(int i=1;i<=m;i++)
        {
            insert(T[i]-‘a‘);
        }
        memset(f,0x3f,sizeof(f));
        f[1]=0;
        int ans=1<<30;
        for(int i=1;i<=n;i++)
        {
            for(int j=cnt;j>=1;j--)
            {
                int now=tr[j][S[i]-‘a‘];
                if(now)
                {
                    f[now]=min(f[now],f[j]+1);
                }
                else
                {
                    ans=min(ans,f[j]+1);
                }
            }
        }
        return ans>n?-1:ans;
    }
};
namespace subtask4
{
    int next[3000][30];
    int f[3000];
    int suf[30];
    int solve()
    {
        for(int i=0;i<26;i++)
        {
            suf[i]=m+1;
        }
        for(int i=m;i>=0;i--)
        {
            for(int j=0;j<26;j++)
            {
                next[i][j]=suf[j];
            }
            suf[T[i]-‘a‘]=i;
        }
        memset(f,0x3f,sizeof(f));
        f[0]=0;
        int ans=1<<30;
        for(int i=1;i<=n;i++)
        {
            for(int j=m;j>=0;j--)
            {
                int now=next[j][S[i]-‘a‘];
                if(now>m)
                {
                    ans=min(ans,f[j]+1);
                }
                else
                {
                    f[now]=min(f[now],f[j]+1);
                }
            }
        }
        return ans>n?-1:ans;
    }
};
int main()
{
    scanf("%s%s",S+1,T+1);
    n=strlen(S+1);
    m=strlen(T+1);
    printf("%d\n",subtask1::solve());
    printf("%d\n",subtask2::solve());
    printf("%d\n",subtask3::solve());
    printf("%d\n",subtask4::solve());
}

BZOJ4032[HEOI2015]最短不公共子串——序列自動機+後綴自動機+DP+貪心