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

2806: [Ctsc2012]Cheat

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 583  Solved: 330
[Submit][Status][Discuss]

Description

Input

第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文

Output

N行,每行一个整数,表示这篇作文的Lo 值。

Sample Input

1 2
10110
000001110
1011001100

Sample Output

4

HINT

输入文件不超过1100000字节

注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%

  先将作文库建后缀自动机,多篇文章可以通过在中间加入分隔符来完成。对于每组询问,预处理出每一个位置向前最多匹配多长g[]。

  二分答案len,dp[]表示匹配到当前位置的最多匹配数,对于i位置,dp[i]由一段通过len,与g确定出的区间[l,r]转移,本来用了一个线段树维护,但是由于时间复杂度O(n*log^2n),TLE了,观察发现[l,r]是单调的,故可直接用单调队列。

  网上一半题解过不了数据:1 1 1 1

  另外,对于0.9的问题确实说明了以后能用int就不要用double,实在不行要加eps。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 4100000
#define MAXT MAXN*4
#define INF 0x3f3f3f3f
#define lch (now<<1)
#define rch (now<<1^1)
#define smid ((l+r)>>1)
char buf[MAXN];
char *bufnow(buf);
int len[MAXN];
char *str[MAXN];
char buf2[MAXN];
char *bufnow2(buf2);
struct sam_node
{
        int nxt[3];
        int pnt,len;
        void Print()
        {
                for (int i=0;i<3;i++)
                        printf("%d[%d] ",i,nxt[i]);
                printf("\n");
                printf("Pnt:%d\n",pnt);
        }
}sam[MAXN];
int tops=1;
int last=1;
void Add_item(int w)
{
        int p=last;
        int np=++tops;
        sam[np].len=sam[p].len+1;
        while (p && !sam[p].nxt[w])
                sam[p].nxt[w]=np,p=sam[p].pnt;
        if (!p)
        {
                last=np;
                sam[np].pnt=1;
        }else
        {
                int q=sam[p].nxt[w];
                if (sam[p].len+1==sam[q].len)
                {
                        sam[np].pnt=q;
                }else
                {
                        int nq=++tops;
                        sam[nq]=sam[q];
                        sam[nq].len=sam[p].len+1;
                        sam[nq].pnt=sam[q].pnt;
                        sam[q].pnt=nq;
                        sam[np].pnt=nq;
                        while (p && sam[p].nxt[w]==q)
                        {
                                sam[p].nxt[w]=nq;
                                p=sam[p].pnt;
                        }
                }
        }
        last=np;
}
int g[MAXN];
int dp[MAXN];
int seq[MAXN];

int main()
{
        freopen("input.txt","r",stdin);
        int n,m;
        int x,y;
        scanf("%d%d\n",&n,&m);
        for (int i=0;i<m;i++)
        {
                scanf("%s\n",bufnow2);
                bufnow2+=strlen(bufnow2);
                *(bufnow2++)=‘2‘;
        }
        for (int i=0;i<n;i++)
        {
                scanf("%s\n",bufnow);
                str[i]=bufnow;
                bufnow+=len[i]=strlen(bufnow);
                bufnow++;
        }
        for (char *i=buf2;i!=bufnow2;i++)
        {
                Add_item(*i-‘0‘);
        }
        for (int i=1;i<=tops;i++)
        {
        //        printf("SAM<%d>:\n",i);
        //        sam[i].Print();
        }
        for (int i=0;i<n;i++)
        {
                int now=1;
                int clen=0;
                for (int j=0;j<len[i];j++)
                {
                        int w=str[i][j]-‘0‘;
                        if (sam[now].nxt[w])
                        {
                                now=sam[now].nxt[w];
                                clen++;
                        }else
                        {
                                while (now && !sam[now].nxt[w])
                                        now=sam[now].pnt;
                                if (!now)
                                {
                                        now=1;
                                        clen=0;
                                }else
                                {
                                        clen=sam[now].len+1;
                                        now=sam[now].nxt[w];
                                }
                        }
                        g[j]=clen;
            //            printf("%d\n",clen);
                }
                for (int j=len[i];j>=1;j--)
                        g[j]=g[j-1];
                g[0]=0;
                int l=0,r=len[i]+1;
                int mid;
                int head,tail=0;
                int ny;
                while (l+1<r)
                {
                        mid=(l+r)>>1;
                        int t;
                        for (int j=1;j<=len[i];j++)
                                dp[j]=-INF;
                        dp[0]=0;
                        head=0,tail=-1;
                        ny=0;
                        for (int j=1;j<=len[i];j++)
                        {
                                x=j-g[j];
                                y=j-mid;
                                while (ny<=y)
                                {
                                        while (head<=tail && dp[seq[tail]]<=dp[ny])
                                                tail--;
                                        seq[++tail]=ny++;
                                }
                                while (head<=tail && seq[head]<x)
                                        head++;
                                dp[j]=dp[j-1]+(j-1);
                                if (head<=tail)
                                        dp[j]=max(dp[j],dp[seq[head]]+j);
                                dp[j]-=j;
                        }
                        t=dp[len[i]]+len[i];
                        if (t*10>=len[i]*9)
                                l=mid;
                        else
                                r=mid;
                }
                printf("%d\n",l);
        }
}
时间: 2024-07-29 20:18:29

bzoj 2806: [Ctsc2012]Cheat 后缀自动机DP的相关文章

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

[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 熟悉的文章

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

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

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】 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

【bzoj3998】[TJOI2015]弦论 后缀自动机+dp

题目描述 对于一个给定长度为N的字符串,求它的第K小子串是什么. 输入 第一行是一个仅由小写英文字母构成的字符串S 第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个.T=1则表示不同位置的相同子串算作多个.K的意义如题所述. 输出 输出仅一行,为一个数字串,为第K小的子串.如果子串数目不足K个,则输出-1 样例输入 aabc 0 3 样例输出 aab 题解 后缀自动机+dp 先对原串建立后缀自动机,然后在其上面跑dp统计每个节点开始的串的个数. 设f[i]表示与位置i有相同前缀的串

bzoj 3998: [TJOI2015]弦论(后缀自动机)

题目链接:bzoj 3998: [TJOI2015]弦论 题意: 对于一个给定长度为N的字符串,求它的第K小子串是什么. 题解: 后缀自动机O(n)*26解决. 对于op=0,num[i]=1,对于op=1,num[i]=cnt[i]. 因为cnt[i](即right集)表示以i节点结尾的后缀出现的次数. 1 #include<cstdio> 2 #include<cstring> 3 #define F(i,a,b) for(int i=a;i<=b;++i) 4 #def

BZOJ 3238 AHOI2013 差异 后缀自动机

题目大意:给定一个字符串,求Σ[1<=i<j<=n]|Ti|+|Tj|-2|LCP(Ti,Tj)| 前两项是可以O(1)求的 我们要求的就是LCP之和 对反串建立后缀自动机 那么parent指针连成的树就是后缀树 直接在后缀树上DP就行- - 对于每个节点统计所有子树两两right集合大小乘积之和乘上这个节点的深度即可 QY神在学校讲了一天的SAM... 现在我觉得我还是回去学大型建筑机械吧233- - #include <map> #include <vector&g