[BZOJ2806] [CTSC2012] Cheat - 后缀自动机 - DP - 单调队列

2806: [Ctsc2012]Cheat

Time Limit: 20 Sec   Memory Limit: 256 MB
Submit: 956   Solved: 494
[ 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%

Source

[ Submit][ Status][ Discuss]

HOME   Back


第一次做CTSC题目就有一半是个模板题……考试遇到这种题就简直爽飞啊。这里只要想到单调队列就可以轻松水出来。(当然本蒟蒻是没想到的orz)

我们这样考虑,每个后缀在末尾打标记,最后拓扑一下,就能够在O(n)的时间内匹配出整个作文每个位置之前能够匹配的长度v。显然地,我们二分答案,设这个值为lim,就要考虑lim是否可行,这里我们能接受的复杂度为O(n),因为匹配完全可以在二分答案前进行。

然后我们考虑dp,很显然有一个dp的式子,就是dp[i]=max{dp[j]+i-j} 其中 j∈[i-v[i],i-lim]

然后我们就会发现,每个点的决策区间的最右端i-lim是递增的。

观察dp的式子,显然我们要选择的应该是前面的空字符个数最小的一个作为解!(这一步超赞)

因此后选择时一定会选择产生的空字符数比i-lim位置要小的,否则我们可以直接选择i-lim也比选择它更优。

那么我们可以考虑一下:我们能否维护一个单调队列,使得队首元素中的空字符数始终是可决策区间中的最少呢?答案是可行的。事实上,我们只需要依次插入i-lim就可以了。

由于我们v[i]的特殊性质,显然,i-v[i]也是不降的。那么我们只要加上队首和队尾的优化就可以了。

/**************************************************************
    Problem: 2806
    User: whzzt
    Language: C++
    Result: Accepted
    Time:912 ms
    Memory:45320 kb
****************************************************************/
 
#include "stdio.h"
#include "algorithm"
#include "iostream"
#include "string.h"
#include "stdlib.h"
#include "math.h"
#include "vector"
#include "map"
#include "set"
#define bup _buff[_pos]
#define nbup _buff[_pos++]
using namespace std;
 
const int L=1100005,A=3;
char _buff[L]; int _pos;
void read(){fread(_buff,L,sizeof(char),stdin);}
inline void read(int&x){
    x=0;while(bup>'9'||bup<'0')++_pos;
    while(bup<='9'&&bup>='0')x=x*10+nbup-'0';
}
inline void read(int*s,int&l){
    l=0;while((bup>>1)^('0'>>1))++_pos;
    while((bup>>1)==('0'>>1))s[++l]=nbup-'0';
}
 
struct SAM {
    int fail[L],e[L][A],dis[L],str[L],len,las,rr,ma[L],ml,v[L],dp[L],q[L],l,r,m,n;
    SAM(){rr=las=1;}
    void extend(int c){
        int p=las,np=++rr;
        dis[np]=dis[p]+1; las=np;
        for(;!e[p][c]&&p;p=fail[p])
            e[p][c]=np;
        if(!p) fail[np]=1;
        else {
            int q=e[p][c],nq;
            if(dis[q]==dis[p]+1)
                fail[np]=q;
            else{
                nq=++rr; dis[nq]=dis[p]+1;
                memcpy(e[nq],e[q],sizeof(int[A]));
                fail[nq]=fail[q];fail[q]=fail[np]=nq;
                for(;e[p][c]==q;p=fail[p])e[p][c]=nq;
            }
        }
    }
    void pre(){
        int i,j,t; read(m),read(n);
        for(i=1;i<=n;i++){
            read(str+len,t);
            for(j=1;j<=t;j++)
                extend(str[++len]);
            extend(str[++len]=2);
        }
    }
    void match(){
        int i,p=1,ans=0;
        for(i=1;i<=ml;i++){
            if(e[p][ma[i]])
                p=e[p][ma[i]],++ans;
            else{
                while(p&&!e[p][ma[i]])p=fail[p];
                if(p)ans=dis[p]+1,p=e[p][ma[i]];
                else ans=0,p=1;
            }
            v[i]=ans;
        }
    }
    bool check(int lim){
        if(lim==0)return true;int i;
        for(i=1;i<=ml;i++)dp[i]=0;
        for(i=l=1,r=0;i<=ml;i++){
            dp[i]=dp[i-1];
            if(i>=lim){
                while(l<=r&&q[r]-dp[q[r]]>i-lim-dp[i-lim])
                    --r;
                q[++r]=i-lim;
            }
            while(l<=r&&q[l]<i-v[i])++l;
            if(l<=r)dp[i]=max(dp[i],dp[q[l]]+i-q[l]); 
        }
        return 10*dp[ml]>=9*ml;
    }
    void work(){
        for(int i=1;i<=m;i++){
            read(ma,ml); match();
            int bt=0,tp=ml,md=(bt+tp)>>1;
            while(bt<tp){
                if(check(md))bt=md;
                else tp=md-1;
                md=(bt+tp+1)>>1;
            }
            printf("%d\n",md);
        }
    }
} sam;
 
int main(){
    read(); sam.pre(); sam.work();
    return 0; 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值