zoukankan      html  css  js  c++  java
  • 【BZOJ2806】熟悉的文章(CTSC2012)-广义SAM+二分+DP+单调队列

    测试地址:熟悉的文章
    做法:本题需要用到广义SAM+二分+DP+单调队列。
    首先,L的性质显然是单调的,所以我们二分L。接下来容易想到DP,令f(i)为以第i个字符结尾的前缀最多能有多少个字符被符合条件的子串覆盖,容易得到状态转移方程:
    f(i)=max(f(i1),f(ik)+k)
    其中f(i1)的转移表示我们不选择用一个子串覆盖一个后缀,而f(ik)+k的转移需要保证ik+1i这一段为M个模式串的一个子串,并且kL
    要判别一个串是不是M个模式串的一个子串,显然使用好写又快的广义SAM,这样我们就可以在DP时顺便将字符串在SAM中匹配,并求出它最长的满足条件的后缀长度。注意满足要求的后缀长度不一定是对应节点能表示的最长的一个子串,因为每次匹配如果匹配上了,最长长度只会加1,不一定会加满。令前i个字符构成的前缀的满足条件的最长长度为len(i),显然长度在len(i)之内的后缀都满足条件。因此上面DP方程的条件可以简写为:
    Lklen(i)
    也即:
    ilen(i)ikiL
    注意到合法的转移构成了一个连续的区间,而这个区间的左右端点都是单调不递减的(ilen(i)一定不递减,因为若i1len(i1)>ilen(i),有len(i)>len(i1)+1,根据定义明显知道不可能出现这种情况),所以可以使用单调队列优化DP。于是我们就完成了这一题,时间复杂度为O(nlogn)
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int n,m,rt=1,last,tot=1;
    int ch[2200010][2]={0},pre[2200010]={0},len[2200010]={0};
    int q[1100010],f[1100010];
    char s[1100010];
    
    void make_clone(int c,int p,int q,int nq)
    {
        len[nq]=len[p]+1;
        ch[nq][0]=ch[q][0];
        ch[nq][1]=ch[q][1];
        while(ch[p][c]==q) ch[p][c]=nq,p=pre[p];
        pre[nq]=pre[q];
        pre[q]=nq;
    }
    
    void extend(int c)
    {
        int p,q,np;
        p=last;
        if (ch[p][c])
        {
            if (len[ch[p][c]]==len[p]+1) last=ch[p][c];
            else make_clone(c,p,ch[p][c],++tot),last=tot;
            return;
        }
    
        np=++tot;
        len[np]=len[p]+1;
        while(!ch[p][c]) ch[p][c]=np,p=pre[p];
        if (!p) pre[np]=rt;
        else
        {
            q=ch[p][c];
            if (len[q]==len[p]+1) pre[np]=q;
            else make_clone(c,p,q,++tot),pre[np]=tot;
        }
        last=np;
    }
    
    bool check(int l,int n)
    {
        f[0]=0;
        int h=1,t=0,now=rt,nowlen=0;
        for(int i=1;i<=n;i++)
        {
            while(now&&!ch[now][s[i]-'0']) now=pre[now],nowlen=len[now];
            if (!now) now=rt;
            else now=ch[now][s[i]-'0'],nowlen++;
            if (i>=l)
            {
                while(h<=t&&f[q[t]]-q[t]<=f[i-l]-(i-l)) t--;
                q[++t]=i-l;
            }
            while(h<=t&&q[h]<i-nowlen) h++;
            if (h>t) f[i]=f[i-1];
            else f[i]=max(f[i-1],f[q[h]]+i-q[h]);
        }
        return 10*f[n]>=9*n;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%s",s);
            int len=strlen(s);
            last=rt;
            for(int j=0;j<len;j++)
                extend(s[j]-'0');
        }
    
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s+1);
            s[0]='#';
            int len=strlen(s)-1;
            int l=0,r=len;
            while(l<r)
            {
                int mid=(l+r)>>1;
                if (check(mid+1,len)) l=mid+1;
                else r=mid;
            }
            printf("%d
    ",l);
        }
    
        return 0;
    }
  • 相关阅读:
    Git——pull拉取远程指定分支以及push到远程指定分支
    Git——拉取远程主分支到本地新建分支,并关联到对应的远程新分支
    Git——基础学习
    Flutter——侧边二级菜单栏
    Flutter——static, final, const 区别
    Git一些常用的指令
    Flutter——切换页面,如何保持当前页的状态
    Flutter—找不到图片&不显示本地图片
    Android Studio快捷方式
    某iOS APP反抓包分析
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793353.html
Copyright © 2011-2022 走看看