• 【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
  • 相关阅读:
    MySQL的Limit 性能差?真的不能再用了?
    天天写order by,你知道Mysql底层如何执行吗?
    微信小程序 rich-text使用正则去除html中img标签中的css样式
    微信小程序开发加入版本更新提示并自动更新
    keepass 用户名显示星号的问题
    firebase/php-jwt使用openssl实现 RSA非对称加密
    Homstead ubuntu 系统pip3的安装
    sqlserver 重置自增列种子值 违反了 PRIMARY KEY 约束的处理
    ghost 安装系统出现EFI PART红色错误的问题
    在laravel 5.6中接管dingo/api 错误
  • 原文地址:https://www.cnblogs.com/onioncyc/p/8192581.html
Copyright © 2020-2023  润新知