zoukankan      html  css  js  c++  java
  • [CTSC2012]熟悉的文章(后缀自动机+动态规划)

    题目描述

    阿米巴是小强的好朋友。

    在小强眼中,阿米巴是一个作文成绩很高的文艺青年。为了获取考试作文的真谛,小强向阿米巴求教。阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范文拼拼凑凑而成的。小强不禁向阿米巴投去了疑惑的眼光,却发现阿米巴露出了一个狡黠的微笑。

    为了有说服力地向阿米巴展示阿米巴的作文是多么让人觉得“眼熟”,小强想出了一个评定作文 “熟悉程度”的量化指标:L 0 .小强首先将作文转化成一个 01 串。之后,小强搜集了各路名家的文章,同样分别转化成 01 串后,整理出一个包含了 M 个 01 串的“ 标准作文库 ”。

    小强认为:如果一个 01 串长度不少于 L 且在 标准作文库 中的某个串里出现过(即,它是 标准作文库 的 某个串 的一个 连续子串 ),那么它是“ 熟悉 ”的。对于一篇作文(一个 01 串)A,如果能够把 A 分割成若干段子串,其中“ 熟悉 ” 的子串的 长度 总 和 不少于 A 总 长度的 90%,那么称 A 是 “ 熟悉的文章 ”。 L 0 是 能够让 A 成为 “ 熟悉的文章 ” 的 所有 L 的最大值 (如果不存在这样的 L,那么规定 L 0 =0)。

    举个例子:

    小强的作文库里包含了如下 2 个字符串:

    10110
    000001110

    有一篇待考察的作文是:

    1011001100

    小强计算出这篇作文 L 的最大值是 4,因为待考察的作文可以视作'10110'+'0110'+'0',其中'10110'和'0110'被判定为 “ 熟悉 ” 的。而当 L = 5 或是更大的时候,不存在符合题意的分割方法。所以,这篇作文的 L 0 = 4。小强认为阿米巴作文的 L 0 值比其他同学的明显要大。请你帮他验证一下。

    题解

    我们可以对模式串建广义SAM,求出文本串的每个前缀与模式串的最长公共后缀。

    这玩意有什么用?

    再继续考虑,答案具有单调性,我们可以外面套个二分。

    然后又转移方程

    dp[i]=max(dp[i-1],dp[j]+i-j)(i-LCS<=j<=i-mid)

    很明显,转移是一个区间,而且这个区间是向右滑动的,所以可以直接上单调队列。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define N 2200009
    using namespace std;
    int l[N],last,len,ch[N][2],cnt,fa[N],q[N],h,t,dp[N],g[N],le[N],n,m;
    char s[N];
    inline void insert(int x){
        if(!ch[last][x]){
        int p=last,np=++cnt;l[np]=l[p]+1;last=np;
        for(;p&&!ch[p][x];p=fa[p])ch[p][x]=np;//??
        if(!p)fa[np]=1;
        else{
            int q=ch[p][x];
            if(l[p]+1==l[q])fa[np]=q;
            else{
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
            }
        }
        }
        else{
            int p=last,q=ch[last][x];
            if(l[p]+1==l[q])last=q;
            else {
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=nq;//!!
                for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
                last=nq;
            }
        }  
    }
    inline void ins(int x){
        while(h<=t&&g[q[t]]<=g[x])t--;
        q[++t]=x;
    }
    inline bool check(int mid,int n){
        h=1,t=0;
        for(int i=0;i<=n;++i)g[i]=dp[i]=0;
        for(int i=1;i<=n;++i){
            dp[i]=dp[i-1];
            int ll=i-le[i],rr=i-mid;ll=min(ll,rr+1);    
            if(rr>=0)ins(rr);
            while(h<=t&&q[h]<ll)h++;
            if(h<=t)dp[i]=max(dp[i],g[q[h]]+i);    
            g[i]=dp[i]-i;
        }
        return 10*dp[n]>=9*n;
    }
    int main(){
        scanf("%d%d",&n,&m);cnt=1;
        for(int i=1;i<=m;++i){
            scanf("%s",s+1);len=strlen(s+1);last=1;
            for(int j=1;j<=len;++j)insert(s[j]-'0');
        }
        while(n--){
            scanf("%s",s+1);len=strlen(s+1);
            int now=1;
            for(int i=1;i<=len;++i){
                if(ch[now][s[i]-'0'])now=ch[now][s[i]-'0'],le[i]=le[i-1]+1; 
                else{
                    while(now&&!ch[now][s[i]-'0'])now=fa[now];
                    if(now)le[i]=l[now]+1,now=ch[now][s[i]-'0'];else now=1;
                }
            }
            int L=1,R=len,ans=0;
            while(L<=R){
                int mid=(L+R)>>1;
                if(check(mid,len))ans=mid,L=mid+1;else R=mid-1;
            } 
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    centOS7虚拟机连接大网
    [CSP-S模拟测试48]反思+题解
    [CSP-S模拟测试47]反思+题解
    [bzoj2456]mode 题解
    [CSP-S模拟测试45]题解
    [CSP模拟测试43、44]题解
    [CSP-S模拟测试41]题解
    [NOIP模拟测试38]题解
    一些idea
    [NOIP模拟测试37]反思+题解
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/10179020.html
Copyright © 2011-2022 走看看