• [POI2005][luogu3462] SZA-Template [fail树]


    题面

    传送门

    思路

    首先,我们观察一下这个要求的“模板串”,发现它有如下性质:

    1.一个模板串$A$是要求的文本串$B$的公共前后缀

    2.如果一个模板串$A$有另一个模板串$B$(也就是$B$可以覆盖A),那么$B$是比$A$更优的一个解

    3.如果模板串$A$可以完全覆盖文本串$B$,那么$A$在$B$中的匹配位置(按照开头算)之间的空格数不会超过$A$的长度

    这三条性质都挺明显的是吧?接下来我们就看看我们能怎么利用它

    如何利用性质一?

    性质一告诉我们,我们可以把文本串的$next$数组(就是$KMP$里面那个)求出来,然后做这样的操作:

    i=n;//n=strlen(文本串)
    while(i) s[++top]=i,i=next[i];
    

    做完这个操作以后栈$s$中就存放了所有可能的模板串长度

    如何利用性质二?

    我们从小到大枚举$s$中的模板串,对于每个模板串,如果它满足性质三,就令这个长度为答案,否则就再增长一点

    同时,我们有一个结论:能被/长度比较长的/符合性质一的/模板串(对于原串满足)/满足性质一的/一个前缀(这个前缀作为文本串满足)/,一定能和/长度比它短的/另一个模板串(此处也是作为文本串)/满足性质一(这句话意思比较绕)

    如何利用性质三?

    我们构建一个链表(双向的),只包含删除操作和求最大空隙操作,那么它的每个操作是$Oleft(1 ight)$的

    那么我们一开始把文本串的所有位置插入链表,每一次更新上一个长度的模板串能满足性质一的、但是当前长度的模板串不能满足性质一的字符位置(也就是把那个点从链表里面删掉),维护最大空隙

    当一个模板串满足性质三的时候,它就是答案了(因为我们是从小到大枚举的,而题目要求最短的那个)

    还剩什么问题?

    我们最后剩下一个问题:如何把匹配$s[i+1]$代表的模板串、但是不匹配$s[i]$代表的模板串的位置找出来?

    这里我们要利用一个新的数据结构(可能不算?),就是$fail$树(也称$next$树)

    $fail$树就是把$fail[i]$(为了方便,以后$next[i]$称作$fail[i]$)和$i$连起来形成的树,以0为根节点

    $fail$树有这样的性质:

    1.点$x$如果是点$y$的祖先,那么$y$代表的前缀的一个公共前后缀为$x$代表的前缀

    2.不在同一子树内的两点代表的前缀不能互相满足性质一

    那么,我们发现,这道题的问题,其实就是从根节点开始往n号节点走一条链,并且每次把当前节点的$fail$树子树中的所有节点标记,统计原串上的最大空隙,如果空隙小于当前节点代表的前缀的长度,就作为答案输出

    所以实际上这是个fail树的题目

    好像不用fail也能做?反正本蒟蒻是想不出来......

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    char a[500010];int n,cnt,maxgap,fail[500010],pre[500010],suc[500010],first[500010],ans[500010],tot;
    struct edge{//fail树上的边
        int to,next;
    }e[500010];
    void add(int u,int v){
        e[++cnt]=(edge){v,first[u]};first[u]=cnt;
    }
    void getfail(){//求next数组(我的代码里叫fail)
        int i,j=0;
        for(i=1;i<n;i++){
            while(j&&(a[i]!=a[j])) j=fail[j];
            j+=(a[i]==a[j]);fail[i+1]=j;
        }
        for(i=1;i<=n;i++) add(fail[i],i);
    }
    void del(int x){//链表删除操作,O(1)
        suc[pre[x]]=suc[x];
        pre[suc[x]]=pre[x];
        maxgap=max(maxgap,suc[x]-pre[x]);suc[x]=pre[x]=0;
    }
    int q[500010];
    void bfs(int s,int avoid){//s的子树中,避开avoid的子树,其余点全部从链表里面删掉
        int u,v,i,head=0,tail=1;q[0]=s;
        while(head<tail){
            u=q[head++];if(u==avoid) continue;
            del(u);
            for(i=first[u];~i;i=e[i].next){
                v=e[i].to;q[tail++]=v;
            }
        }
    }
    int main(){
        memset(first,-1,sizeof(first));memset(fail,0,sizeof(fail));int i,j;
        scanf("%s",a);n=strlen(a);
        getfail();
        for(i=n;i;i=fail[i]) ans[++tot]=i;ans[tot+1]=0;
        for(i=1;i<=n;i++) pre[i]=i-1,suc[i]=i+1;
        maxgap=1;
        for(i=tot;i>=1;i--){
            bfs(ans[i+1],ans[i]);
            if(maxgap<=ans[i]){
                printf("%d",ans[i]);return 0;
            }
        }
    }
    
  • 相关阅读:
    eclipse如何卸载adt插件
    Android中的Toast.LENGTH_SHORT
    Frogger
    - Oil Deposits 深搜,就是所谓的dfs
    Aggressive cows
    Phone List
    Word Amalgamation
    Street Numbers
    Charm Bracelet——背包问题
    函数参考
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/8903324.html
Copyright © 2020-2023  润新知