zoukankan      html  css  js  c++  java
  • BZOJ4032[HEOI2015]最短不公共子串——序列自动机+后缀自动机+DP+贪心

    题目描述

     在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。

    一个串的“子串”指的是它的连续的一段,例如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
    ",subtask1::solve());
        printf("%d
    ",subtask2::solve());
        printf("%d
    ",subtask3::solve());
        printf("%d
    ",subtask4::solve());
    }
  • 相关阅读:
    调试代码的技巧
    关联Lable和输入项
    如何更有效的利用自己的时间
    高效能人士的七个习惯>读书笔记之二
    .NET别名机制简介
    VS2005升SP1错误1718文件FileName被数字签名策略拒绝
    程序物语(三):做人、做事、生活
    程序物语(二):起手式
    SQL Server 2008如何压缩日志(log)文件?
    prototype中文参数乱码解决方案
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10578358.html
Copyright © 2011-2022 走看看