bzoj 2806: [Ctsc2012]Cheat【广义SAM+二分+dp+单调队列】

把模板串建一个广义SAM

然后在线查询,每次在SAM上预处理出一个a[i]表示i位置向前最多能匹配多长的模板串

二分答案L,dp判断,设f[i]为·~i有几个匹配,转移显然是f[i]=max{f[i-1],f[j]+i-j(i-a[i]<=j<=i-L)},根据性质,i-a[i]是单调的(或者直接看a[i]的预处理过程也能看出来),所以这个dp可以用单调队列优化转移,最后判断是否f[n]>=L*0.9

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3000005;
int n,m,fa[N],ch[N][2],dis[N],cur=1,con=1,la,a[N],q[N],f[N];
char s[N];
void ins(int c,int id)
{
    la=cur,dis[cur=++con]=id;
    int p=la;
    for(;p&&!ch[p][c];p=fa[p])
        ch[p][c]=cur;
    if(!p)
        fa[cur]=1;
    else
    {
        int q=ch[p][c];
        if(dis[q]==dis[p]+1)
            fa[cur]=q;
        else
        {
            int nq=++con;
            dis[nq]=dis[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[nq]));
            fa[nq]=fa[q];
            fa[q]=fa[cur]=nq;
            for(;ch[p][c]==q;p=fa[p])
                ch[p][c]=nq;
        }
    }
}
bool ok(int w,int n)
{
    int l=1,r=0;//cerr<<w<<endl;
    for(int i=1;i<=n;i++)
    {
        f[i]=f[i-1];
        while(l<=r&&q[l]<i-a[i])
            l++;
        if(l<=r)
            f[i]=max(f[i],f[q[l]]+i-q[l]);
        if(i-w+1>=0)
        {
            while(l<=r&&f[q[r]]-q[r]<=f[i-w+1]-(i-w+1))
                r--;
            q[++r]=i-w+1;
        }//cerr<<i<<" "<<f[i]<<endl;
    }
    return f[n]*10>=n*9;
}
int main()
{
    scanf("%d%d",&m,&n);
    for(int j=1;j<=n;j++)
    {
        scanf("%s",s+1);
        cur=1;
        for(int i=1,len=strlen(s+1);i<=len;i++)
            ins(s[i]-‘0‘,i);
    }
    while(m--)
    {
        scanf("%s",s+1);
        int len=strlen(s+1);
        for(int i=1,l=0,p=1;i<=len;i++)
        {
            int c=s[i]-‘0‘;
            if(ch[p][c])
                l++,p=ch[p][c];
            else
            {
                for(;p&&!ch[p][c];p=fa[p]);
                if(!p)
                    l=0,p=1;
                else
                    l=dis[p]+1,p=ch[p][c];
            }
            a[i]=l;//cerr<<i<<" "<<a[i]<<endl;
        }
        int l=0,r=len,ans=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(ok(mid,len))
                l=mid+1,ans=mid;
            else
                r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/lokiii/p/10009223.html

时间: 2024-08-04 06:01:55

bzoj 2806: [Ctsc2012]Cheat【广义SAM+二分+dp+单调队列】的相关文章

【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)

2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1262  Solved: 643 Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数接下来M行的01串,表示标准作文库接下来N行的01串,表示N篇作文 Output N行,每行一个整数,表示这篇作文的Lo 值. Sample Input 1 2 10110 000001110 1011001100 Sam

[bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)

偷懒直接把bzoj的网页内容ctrlcv过来了 2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1943  Solved: 1004[Submit][Status][Discuss] Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数接下来M行的01串,表示标准作文库接下来N行的01串,表示N篇作文 Output N行,每行一个整数,表示这篇作文的Lo

bzoj 2806: [Ctsc2012]Cheat 后缀自动机DP

2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 583  Solved: 330[Submit][Status][Discuss] Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库 的行数 接下来M行的01串,表示标准作文库 接下来N行的01串,表示N篇作文 Output N行,每行一个整数,表示这篇作文的Lo 值. Sample Input 1 2 101

BZOJ 2806 [Ctsc2012]Cheat ——后缀自动机 单调队列优化DP

先建出广义后缀自动机. 然后跑出文章中每一个位置的最大匹配距离. 然后定义$f[i]$表示匹配到以$i$结尾的串时,最长的匹配距离. 显然可以二分$L$的取值. 然后容易得到$DP$方程 $f[i]=max(f[i-1],f[j]+i-j)(j<=i-L)$ 然后就发现$j$属于一个区间,然后就可以单调队列优化了. #include <map> #include <ctime> #include <cmath> #include <queue> #in

bzoj 2806: [Ctsc2012]Cheat

传送门 好久没刷bzoj惹-- 题意不说可以嘛. 首先二分答案. SAM的事情搞完以后就是dp辣. 我们已经对于每个位置i,找到了最小的一个k,使得[k,i]这个子串在模版串中出现过.那么我们需要做的是把f[i]给min上f[k]到f[i-x],直接搞是$n^2logn$的,套个数据结构也是两个log的.然而如果一个位置j不在合法的区间中,那么以后也不会进入,那么直接用一个单调队列维护就好了. #include<cstdio> #include<cstring> #include&

bzoj 2806: [Ctsc2012]Cheat 熟悉的文章

[题意] [题解] fhq自己写的题解就很清楚啦 (里面有关于神秘的阿米巴同学的介绍哦!很帅的样子,,, 我再来理一下思路 这道题思路应该是很简单很常规的,,但我做题太少辣! 二分一下肯定是必要的, 下面是判断是否可行的问题. 容易想到 要开一个 f 数组 表示 扫到了 第 i 位时 能匹配上多少个字母, 最后答案就是 f[lenth] * 10 >= lenth * 9 啦 f[i] 显然可以 通过 f[j] 转移 (j <= i - 二分出的下限的长度 + 1  且s[j……i]出现在字典

复习【dp+单调队列】

处理10W数据:二分答案+单调队列+dp 这题可以用二维Dp和一维Dp来做,虽然两者时间复杂度都是O(N2),但二维的无法被优化,一维的可以用单调队列将时间效率缩为O(N).因此构造Dp时优先采用纬度小的方案. 单调队列的核心模板见代码. ps:在一部分单调队列的题目中,队列各项的数值会同时变化.此时应用变量Plus做维护 #include <iostream> using namespace std; struct qnode { long tim, x; }; const long N=1

习题:烽火传递(DP+单调队列)

烽火传递[题目描述]烽火台又称烽燧,是重要的防御设施,一般建在险要处或交通要道上.一旦有敌情发生,白天燃烧柴草,通过浓烟表达信息:夜晚燃烧干柴,以火光传递军情.在某两座城市之间有n个烽火台,每个烽火台发出信号都有一定的代价.为了使情报准确的传递,在m个烽火台中至少要有一个发出信号.现输入n.m和每个烽火台发出的信号的代价,请计算总共最少需要花费多少代价,才能使敌军来袭之时,情报能在这两座城市之间准确的传递!!![输入描述]第一行有两个数n,m(1<=n,m<=1000000)分别表示n个烽火台

poj3017 dp+单调队列

http://poj.org/problem?id=3017 Description Given an integer sequence { an } of length N, you are to cut the sequence into several parts every one of which is a consecutive subsequence of the original sequence. Every part must satisfy that the sum of