BZOJ2806_Cheat
Ctsc2012的题目。做完感觉自己瞬间变高富帅了。
不过回想其实也觉得不难,想到用单调队列就很简单了,还有二分= =。呵
对于给出的一篇文章,如果你们将它分成若干段,并在所有长度不小于L的片段在字典中间出现的总长度和不小于原文的90%,那么这篇文章就可以被认为是熟悉的。这里要注意理解一下题意,如果你要算对于L这个长度熟悉。那么小于L的片段就不要去判断了,它肯定不熟悉。
这样我们就可以二分了,每次二分一个长度,看看能否使文章熟悉就好,最终输出答案。
怎么判断当前这个二分的答案能否使得文章熟悉呢?
其实这个问题,就是求把文章分段后的最大的匹配长度。这样由于存在策略最优的问题,而且显然可以递推,就是DP了。
如何DP呢?f[i]表示前面i个字符的最大匹配长度。对于当前我们考虑的状态,我们只要考虑它为某一段尾部最后一个字符就可以了。
同时后缀自动机可以在线秒算当前位置的最大匹配长度,假设i位置的最大匹配长度为len。
首先,f[i]=f[i-1],因为i个位置的匹配长度肯定不会小于前一个位置的匹配长度。
其次,如果当前位置的匹配长度小于当前枚举的这个L值的话,当前状态也是不予考虑的。因为即使当做尾部最后一个字符也不算数。
再其次,当前状态的决策区间只可能是[i-len,i-L]。因为小于i-len的位置是不能匹配的。
再其次,每次进入一个点,我们都进行队尾的优化,可以优化么?可以。根据前面总共有多少个位置匹配不上来优化,如果一个前面的位置,落下的无法匹配的字符比后面一个位置的空字符还要多,那么它肯定不会在后面的决策总使用,直接从队尾拿出来就好了。
再其次,队首的优化就是判断队首的点时候在决策区间内,如果在的话,当前状态就一定是最优的了。也就是f[j]+i-j。因为在决策区间里的位置,后面的i-j的长度都是可以完美当做一段去匹配的。
再其次,没有了,直接判断是否满足90%的条件即可。
怒赞,ctsc题目果然做起来感觉不一样。
召唤代码君:
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 2502000
using namespace std; int next[maxn][3],pre[maxn],step[maxn];
int p,q,np,nq,cur,len,L;
int f[maxn];
int N,last,n,m;
int Q[maxn],bot,top;
char s[maxn]; void insert(int x)
{
np=++N,p=last,step[np]=step[p]+1,last=np;
for (; p!=-1 && next[p][x]==0; p=pre[p]) next[p][x]=np;
if (p==-1) return;
q=next[p][x];
if (step[q]==step[p]+1) { pre[np]=q; return; }
nq=++N,step[nq]=step[p]+1,pre[nq]=pre[q];
for (int i=0; i<3; i++) next[nq][i]=next[q][i];
pre[np]=pre[q]=nq;
for (; p!=-1 && next[p][x]==q; p=pre[p]) next[p][x]=nq;
} bool check(int limit)
{
bot=1,top=0,f[0]=cur=len=0;
for (int i=1; s[i]; i++)
{
f[i]=f[i-1];
int k=s[i]-'0';
for (; cur!=-1 && next[cur][k]==0; cur=pre[cur]) ;
if (cur==-1) { cur=len=0; }
len=min(len,step[cur])+1;//当前状态的最大匹配长度
cur=next[cur][k]; p=i-limit;
if (p>=0)
{
while (bot<=top && Q[top]-f[Q[top]]>p-f[p]) top--;
Q[++top]=p;
}
while (bot<=top && Q[bot]<i-len) bot++;
if (bot<=top) f[i]=max(f[i],f[Q[bot]]+i-Q[bot]); }
return f[L]*10>=L*9;
} int main()
{
pre[0]=-1;
scanf("%d%d",&n,&m);
while (m--)
{
scanf("%s",s);
for (int i=0; s[i]; i++) insert(s[i]-'0');
insert(2);
}
while (n--)
{
scanf("%s",s+1);
L=strlen(s+1);
int l=0,r=L,mid;
while (l<r)
{
mid=(l+r+1)/2;
if (check(mid)) l=mid;
else r=mid-1;
}
printf("%d\n",l);
}
return 0;
}
BZOJ2806_Cheat的更多相关文章
随机推荐
- DIV样式汇总
DIV样式汇总 最近在学习JavaScript,在做到个要控制控件样式的例子时,突然有了把常用样式汇总一下的想法,于是乎就写了以下内容,以下是以div为例来汇总的,希望对大家有些帮助. 一.常用属性: ...
- 开源项目CIIP(企业信息管理系统框架).2018.1.0910版更新介绍-上周工作总结
又狂撸了一周的代码.简化了0904版本的多数操作. 上一次更新时,总共需要10步,这次简化成3步.嗯嗯,自我感觉不错. 重要的:在创建项目时,可以选择常用模块啦! 第一步:启动CIIP.Designe ...
- linux for 循环的小应用
[root@localhost ~]# mkdir -pv /home/data{1..5} # 创建多个目录 以下两种方法类似. for i in {1..5};do echo "&l ...
- x5webview 自定义全屏界面
集成X5WEBVIEW可以选择全屏模式为标准全屏还是x5全屏,而不设置默认为false. 首先看看标准全屏的基本设置, if (webView.getX5WebViewExtension() != n ...
- linux增加硬盘 磁盘分区格式化及挂载
nux磁盘分区格式化及挂载 意义: 给linux 系统服务器扩容, 加一块硬盘 实验环境: virtualBox虚拟软件 + centos6.5 第一步: 添加硬件 硬盘 (我这里用virtualB ...
- 关于Myeclipse的MyEclipse:Java was started but returned exit code=-1 错误
我们在安装MyEclipse后有时会遇到这样一个问题,可以进入主界面软件也属于激活状态,但是过一会会报错, 并弹出MyEclipse:Java was started but returned exi ...
- 学习java常见dos命令
在java基础学习阶段时一般会在dos命令行下操作文件,这里列出一些常用命令. 打开DOS控制台的方式 按win+r 再输入 cmd 然后回车. 常用DOS命令 d: 回车 盘符切换 (盘符加冒号) ...
- zabbix-agent报错:zabbix_agentd [5922]: cannot open log: cannot create semaphore set: [28] No space left on device
起因: 新添加完自定义监控项后重启zabbix-agent时失败,查看/var/log/zabbix/zabbix-agentd.log 发现系统提示zabbix共享内存报错 zabbix_agent ...
- VMware Workstation and Device/Credential Guard are not compatible
VMware Workstation and Device/Credential Guard are not compatible. VMware Workstation can be run aft ...
- 微信小程序自定义 tabbar
一定的需求情况下,无法使用小程序原生的 tabbar 的时候,需要自行实现一个和 tabbar 功能一模一样的自制组件. 查阅了海量的博客和文档之后,亲自踩坑.总结了三种在不使用微信小程序原生 tab ...