2806: [Ctsc2012]Cheat
Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 956 Solved: 494
[ Submit][ Status][ Discuss]
Description
Input
第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文
Output
N行,每行一个整数,表示这篇作文的Lo 值。
Sample Input
10110
000001110
1011001100
Sample Output
HINT
输入文件不超过1100000字节
注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%
Source
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;
}