zoukankan      html  css  js  c++  java
  • 【BZOJ】4032: [HEOI2015]最短不公共子串(LibreOJ #2123)

    【题意】给两个小写字母串A,B,请你计算:

    (1) A的一个最短的子串,它不是B的子串

    (2) A的一个最短的子串,它不是B的子序列

    (3) A的一个最短的子序列,它不是B的子串

    (4) A的一个最短的子序列,它不是B的子序列

    不存在输出-1,1<=len(A),len(B)<=2000。

    【算法】后缀自动机+序列自动机

    【题解】虽然网上题解很多,但我总觉得这四个问题其实可以一个统一的形式来回答。因为字符串的自动机本质是相同的。

    对串B建立后缀自动机来识别子串,建立序列自动机来识别子序列,从左到右枚举A串并在B自动机上进行。(序列自动机没有fail边,但这里不需要)

    先考虑识别串A的子序列,设$f_x$表示自动机中节点x识别到的A的最短子序列。

    对于A的子序列,从左到右枚举当前字母c,对B自动机中的每个节点都进行转移,假设x+c=y,那么:

    $$f_y=min{ f_y,f_x+1}$$

    如果y=null,那么贡献答案$ans=min{ ans,f_x+1}$。

    原理是:字母c可以接在自动机识别了的所有子序列的后面形成新的子序列。

    这里要注意更新顺序,为了满足无后效性,序列自动机要从后往前更新,后缀自动机要按Parent树从下往上更新(trans边不可能返祖)。

    在考虑识别串A的子串,c只能接在所有以c前一位结尾的子串后面,那么只要每次转移到$f_y$时初始化$f_x=inf$即可。另外注意根节点不能置为inf(要接新子串)。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=2010,inf=0x3f3f3f3f;
    int n,m,last,size,root,pre[maxn],ch[maxn][26],f[maxn*2],w[maxn],b[maxn*2];
    char s[maxn],a[maxn];
    struct tree{int len,fa,t[26];}t[maxn*2];//
    void insert_SAM(int c){
        int np=++size;
        t[np].len=t[last].len+1;
        int x=last;
        last=np;
        while(x&&!t[x].t[c])t[x].t[c]=np,x=t[x].fa;
        if(!x)t[np].fa=root;else{
            int y=t[x].t[c];
            if(t[y].len==t[x].len+1)t[np].fa=y;else{
                int nq=++size;
                t[nq]=t[y];//
                t[nq].len=t[x].len+1;
                t[nq].fa=t[y].fa;t[y].fa=t[np].fa=nq;
                while(x&&t[x].t[c]==y)t[x].t[c]=nq,x=t[x].fa;//
            }
        }
    }
    void build(){
        last=size=root=1;
        for(int i=1;i<=m;i++)insert_SAM(s[i]-'a');
        for(int i=1;i<=m;i++){
            int c=s[i]-'a';
            for(int j=i-1;j>=pre[c];j--)ch[j][c]=i;
            pre[c]=i;
        }
        for(int i=1;i<=size;i++)w[t[i].len]++;
        for(int i=1;i<=m;i++)w[i]+=w[i-1];
        for(int i=1;i<=size;i++)b[w[t[i].len]--]=i;
    }
    int trans(int x,int c,int y){
        if(!y)return t[x].t[c];
        else return ch[x][c];
    }
    void solve(int A,int B){
        memset(f,0x3f,sizeof(f));
        f[B^1]=0;
        int ans=inf;
        for(int i=1;i<=n;i++){
            int c=a[i]-'a';
            for(int z=(B?m:size);z>=(B^1);z--){
                int x=B?z:b[z];
                int y=trans(x,c,B);
                if(!y)ans=min(ans,f[x]+1);else{
                    f[y]=min(f[y],f[x]+1);if(!A&&x!=(B^1))f[x]=inf;
                }
            }
        }
        printf("%d
    ",ans==inf?-1:ans);
    }        
    int main(){
        scanf("%s%s",a+1,s+1);n=strlen(a+1);m=strlen(s+1);
        build();
        solve(0,0);solve(0,1);solve(1,0);solve(1,1);
        return 0;
    }
    View Code
  • 相关阅读:
    c# 得到 XML的节点值和属性值
    jquery 更改超链接
    row_number()得到中间的信息
    UML系统分析与设计03-软件需求分析说明书
    UML系统分析与设计02-用例图和活动图(下)
    从Klocwork中.net检测项说说代码书写
    WCF之诡异通讯超时
    《Beginning WF : Windows Workflow in .NET 4.0》读书笔记
    UML系统分析与设计01-准备
    UML系统分析与设计02-用例图和活动图(上)
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8192581.html
Copyright © 2011-2022 走看看