zoukankan      html  css  js  c++  java
  • P5334 [JSOI2019]节日庆典 扩展KMP

    题意:

    戳这里

    分析:

    • 暴力:

    对于每一个前缀跑一遍最小表示法,复杂度 (O(n^2)),期望得分 (30pts) ~ (50pts)

    我原本以为这个东西可以用lyndon分解补字符做到(O(n)),但是lyndon分解求最小表示法要把原串倍增一下,这样每次相当于是插入两个字符,做不了/kk

    • 正解:

    我们维护一个集合 (point) 记录那些能够作为最小表示法起点的位置,即是lyndon串的后缀的起点,可以发现一个点只会加入集合一次,也就是说一个点一旦无法成为最小表示法的起点,那么它就再也无法加入集合

    假设当前枚举到字符 (s[i]) ,我们考虑 (point) 集合里的元素满足什么关系:

    1. 对于 (forall x<y) 如果 (lcp(s[x:n],s[y:n])le i-y) ,那么 (x,y) 中必然有一个节点不合法

      证明很显然,我们考虑新加入字符 (s[i]) 时,对于原有集合 (point) 集合内元素一定满足 (forall x,y) (lcp(s[x:n],s[y:n])> i-1-y) 而我们要给每个不合法元素 (x) 找到一个元素 (y) 满足 (lcp(s[x:n],s[y:n])le i-y)

      显然我们可以直接比较 (s[x+i-y])(s[i]) 的关系,但是枚举点对是 (O(n^2)) 的,对于每个前缀要是都这么干一次,还不如暴力,所以我们考虑对于每一个 (y) 检验哪些 (x) 不合法时,只需要找到最小的一个 (x) 就可以了,因为集合内其他元素一定是 (x) 的后缀的前缀,一旦它合法,等价于它的前缀都合法,即剩余元素都是合法的

    2. 对于 (forall x<y) 满足 (lcp(s[x:n],s[y:n])> i-y) ,如果 (i-yge y-x)那么 (y) 必然不合法

      证明:假设原串可以表示为:ABBC ,其中 C 是 B 的严格非空前缀,那么 (x,y,z) 分别代表的循环同构串是 BBCA,BCAB,CABB,如果 BC > CA 那么 (y) 不如 (z) 优秀,如果 BC < CA 那么 (y) 不如 (x) 优秀,所以可以直接放弃 (y) ,同时我们可以发现,任意两个元素之间的长度大于 2 倍,所以集合内元素的个数是 (O(log))

    这样我们就能做到 (O(log)) 的在加入一个字符时维护最优决策点的集合,然后我们每一次遍历集合,把前缀接上进行比较,找字典序更小的一个决策点作为答案,由于需要比较每一个后缀和原串的字典序,所以上EXKMP,总复杂度 (O(nlog n))

    代码:

    #include<bits/stdc++.h>
    #define inl inline
    #define reg register
    #define pb push_back
    
    using namespace std;
    
    namespace zzc
    {
        const int maxn = 3e6+5;
        int z[maxn];
        int n;
        char s[maxn];
        vector<int> point;
    
        void exkmp()
        {
            z[1]=n;
            for(reg int i=2,l=0,r=0;i<=n;i++)
            {
                if(i<=r) z[i]=min(z[i-l+1],r-i+1);
                while(i+z[i]<=n&&s[i+z[i]]==s[z[i]+1]) z[i]++;
                if(i+z[i]-1>r) r=i+z[i]-1,l=i;
            }
        }
        
        inl int compare(int x,int len)
        {
            return z[x]>=len?0:(s[x+z[x]]>s[1+z[x]]?-1:1);
        }
    
        inl int check(int x,int y,int len)
        {
            int flag;
            if((flag=compare(y+len-x+1,x-y))!=0) return flag>0?y:x;
            if((flag=compare(x-y+1,y-1))!=0) return flag>0?x:y;
            return y;
        }
    
        void work()
        {
            scanf("%s",s+1);n=strlen(s+1);
            exkmp();
            for(reg int i=1;i<=n;i++)
            {
                vector<int> newpoint(1,i);
                for(auto x:point)
                {
                    while(!newpoint.empty()&&s[x+i-newpoint.back()]<s[i]) newpoint.pop_back();//性质1
                    if(newpoint.empty()||s[x+i-newpoint.back()]==s[i])
                    {
                        while(!newpoint.empty()&&i-newpoint.back()>=newpoint.back()-x) newpoint.pop_back();//性质2
                        newpoint.pb(x);
                    }
                }
                point=newpoint;
                int ans=point[0];
                for(reg int j=1,k=point.size();j<k;j++) ans=check(ans,point[j],i);
                printf("%d ",ans);
            }
        }
    
    }
    
    int main()
    {
        zzc::work();
        return 0;
    }
    
  • 相关阅读:
    Android源码剖析之Framework层进阶版(Wms窗口管理)
    如何让项目中的代码更易于维护
    Android源码剖析之Framework层实战版(Ams管理Activity启动)
    node.js学习路线图
    让你的公众号拥有AI能力--微信对话开放平台
    Android跨平台投屏软件(无需root)--scrcpy
    微信H5支付申请相关问题
    Bmob后端云实现无后端开发APP
    微信公众号申请相关问题
    iOS企业包下载安装
  • 原文地址:https://www.cnblogs.com/youth518/p/14419375.html
Copyright © 2011-2022 走看看