bzoj2806 [Ctsc2012]Cheat

78 篇文章 0 订阅
22 篇文章 0 订阅

http://www.elijahqi.win/archives/3153
题目描述

阿米巴是小强的好朋友。

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

为了有说服力地向阿米巴展示阿米巴的作文是多么让人觉得“眼熟”,小强想出了一个评定作文 “熟悉程度”的量化指标: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 值比其他同学的明显要大。请你帮他验证一下。

输入输出格式

输入格式:

输入文件 cheat.in 第一行是两个整数 N, M,表示待检查的作文数量,和小强的标准作文库的行数。

接下来是 M 行的 01 串,表示标准作文库。

接下来是 N 行的 01 串,表示 N 篇作文。

输出格式:

输出文件 cheat.out 包含 N 行,每一行包含一个整数,表示该篇作文的 L 0 值。

输入输出样例

输入样例#1: 复制

1 2
10110
000001110
1011001100
输出样例#1: 复制

4
说明

对于 30%的测试数据,输入文件的长度不超过 1000 字节。

对于 50%的测试数据,输入文件的长度不超过 61000 字节。

对于 80%的测试数据,输入文件的长度不超过 250000 字节。

对于 100%的测试数据,输入文件的长度不超过 1100000 字节。

考虑 多个串建广义SAM即可 然后针对每个询问的串首先二分一下 答案 然后dp一下判断是否可行

二分的答案就是L 即切割的相同的子串最短是多少 设dp[i]表示以i结尾的串 的最大符合要求的匹配长度

那么每次这个答案可以考虑用单调队列优化 队头是每次根据当前匹配的最大长度不断往外弹出 而队尾则是根据没有匹配的长度来进行弹出的 为什么 因为我的dp[i]肯定是单调不降的 若我后面的满足>=L这个条件 并且我没有匹配的还比之前的小 那说明答案以后在转移的时候显然会选我而不是现在的队尾所以弹出

一开始自己写萎了写的是每次将i插入队尾 这样有一个什么错误就是在于 我当前的i插进去但是这个i不能马上起更新答案的效果 同时他还有可能将之前合法的答案认为不优直接弹出了 造成答案错误

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1200000;
int last=1,root=1,cnt=1,ch[N<<1][2],len[N<<1],fa[N<<1];
inline void insert1(int x){
    int p=last;
    if(ch[p][x]){
        int q=ch[p][x];if (len[p]+1==len[q]) last=q;else{
            int nq=++cnt;len[nq]=len[p]+1;memcpy(ch[nq],ch[q],sizeof(ch[q])); 
            fa[nq]=fa[q];fa[q]=nq;for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;last=nq;
        }
    }else{
        int np=++cnt;len[np]=len[p]+1;
        for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
        if (!p) fa[np]=root;else{
            int q=ch[p][x];if(len[p]+1==len[q]) fa[np]=q;else{
                int nq=++cnt;len[nq]=len[p]+1;memcpy(ch[nq],ch[q],sizeof(ch[q]));
                fa[nq]=fa[q];fa[q]=fa[np]=nq;for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
            }
        }last=np;
    }
}
int q[N],dp[N];
inline bool check(char *s,int n,int L){
    int op=1,cl=0,le=0,p=root;
    for (int i=1;i<=n;++i){
        dp[i]=dp[i-1];
        if(ch[p][s[i]-'0']) p=ch[p][s[i]-'0'],++le;
        else {
            for (;p&&!ch[p][s[i]-'0'];p=fa[p]);if (!p) p=root,le=0;
            else le=len[p]+1,p=ch[p][s[i]-'0'];
        }int l=i-L;
        //printf("%d ",le);
        if (l>=0){
            while(op<=cl&&l-dp[l]<q[cl]-dp[q[cl]]) --cl;q[++cl]=l;
        }while(op<=cl&&q[op]<i-le) ++op;
        if (op<=cl) dp[i]=max(dp[i],dp[q[op]]+i-q[op]); 

    }
//  for (int i=1;i<=n;++i) printf("%d ",dp[i]);puts("");
    return dp[n]*10>=n*9;
}
int n,m;char s[N]; 
int main(){
    freopen("bzoj2806.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;++i) {
        scanf("%s",s);last=root;//printf("%s\n",s);
        for (int i=0;s[i];++i) insert1(s[i]-'0');//printf("%c",s[i]);puts("");
    }
    for (int i=1;i<=n;++i){
        scanf("%s",s+1);int le=strlen(s+1);
        int l=1,r=le;
        while(l<=r){
            int mid=l+r>>1;
            if(check(s,le,mid)) l=mid+1;else r=mid-1;//puts("");
        }printf("%d\n",r);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值