BZOJ2806 [Ctsc2012]Cheat 【后缀自动机 + 二分 + 单调队列优化DP】
题目


输入格式
第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库
的行数
接下来M行的01串,表示标准作文库
接下来N行的01串,表示N篇作文
输出格式
N行,每行一个整数,表示这篇作文的Lo 值。
输入样例
1 2
10110
000001110
1011001100
输出样例
4
提示
输入文件不超过1100000字节
注意:题目有改动,可识别的长度不小于90%即可,而不是大于90%
题解
想来练练SAM,却跪在了单调队列DP上。。。QAQ
根据后缀数组进行多串匹配时,用一个未出现的字符将各串连接起来形成一个串
我们可以照搬到后缀自动机来,对m个模板串建立后缀自动机
此时我们可以在后缀自动机上走一遍求出匹配串每个位置为结尾所能匹配的最大长度【见代码init】
记为len[i],即表示匹配的子串\([i - len[i] + 1,i]\)可以与模板串匹配
有了这些,我们如何计算\(L_0\)?
仔细思考发现L具有单调性,即在长度至少为L时选择的最小切割方案下,对长度小于L的限制仍然满足,长度小于L的可以照搬L的切割方法而满足条件
这个时候我们就可以二分了
对于长度L,我们可以用DP检验,令f[i]表示前i个字符在L限制下能匹配的最大长度
首先f[i]至少等于f[i - 1],因为i可以单独被化为出来而转移到f[i - 1]【也就是说f[i]单调不下降,这个待会会用到】
我们有状态转移方程:\(f[i] = max{f[j] + i - j},j\in [i - len[i],i - L]\)
解释:由于L的限制,要切割出以i结尾产生贡献的区间,必须在i - L及之前,而i最多匹配到i - len[i] + 1,所以\(j\in [i - len[i],i - L]\)
最后的匹配长度就是i割出这段的长度 + f[j]
这样做是\(O(n^2logn)\)的,不能满足题意
考虑优化
我们将方程变形\(f[i] - i = f[j] - j\),左边是只与i有关的量,与决策无关
右边是只与j有关的量,由f[j]的定义,f[j] - j表示前j个没匹配的个数【负数】,由于没匹配的个数不会变少,所以f[j] - j单调递减
也就是说决策具有单调性,且只与j有关,可以采用单调队列优化
【一定要好好写队列QAQ,WA了几发】
呼啦啦~~~搞完了【CTSC的题目哇。】
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 3000005,maxm = 100005,INF = 1000000000;
int pre[maxn],ch[maxn][3],step[maxn],cnt,last;
char s[maxn];
void ins(int x){
int p = last,np = ++cnt;
last = np; step[np] = step[p] + 1;
while (p && !ch[p][x]) ch[p][x] = np,p = pre[p];
if (!p) pre[np] = 1;
else {
int q = ch[p][x];
if (step[q] == step[p] + 1) pre[np] = q;
else {
int nq = ++cnt; step[nq] = step[p] + 1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
pre[nq] = pre[q]; pre[q] = pre[np] = nq;
while (ch[p][x] == q) ch[p][x] = nq,p = pre[p];
}
}
}
int q[maxn],head,tail,f[maxn],N,len[maxn];
bool check(int L){
head = tail = f[0] = 0;
for (int i = 1; i <= N; i++){
f[i] = f[i - 1];
int l = i - len[i],r = i - L;
if (r >= 0){
while (head < tail && f[q[tail - 1]] - q[tail - 1] < f[r] - r) tail--;
q[tail++] = r;
}
while (head < tail && q[head] < l) head++;
if (head < tail) f[i] = max(f[i],f[q[head]] - q[head] + i);
}
return 10 * f[N] >= 9 * N;
}
void init(){
int u = 1,id,ans = 0;
for (int i = 1; i <= N; i++){
id = s[i] - '0';
if (ch[u][id]) len[i] = ++ans,u = ch[u][id];
else {
while (u != 1 && !ch[u][id]) u = pre[u];
if (u == 1) len[i] = ans = 0;
else len[i] = ans = step[u] + 1,u = ch[u][id];
}
}
}
int main(){
int n,m; last = cnt = 1;
scanf("%d%d",&n,&m);
while (m--){
scanf("%s",s); N = strlen(s);
for (int i = 0; i < N; i++) ins(s[i] - '0');
ins(2);
}
while (n--){
scanf("%s",s + 1); N = strlen(s + 1);
init();
int l = 0,r = N,mid,ans = 0;
while (l <= r){
mid = l + r >> 1;
if (check(mid)) l = mid + 1,ans = mid;
else r = mid - 1;
}
printf("%d\n",ans);
}
return 0;
}
BZOJ2806 [Ctsc2012]Cheat 【后缀自动机 + 二分 + 单调队列优化DP】的更多相关文章
- BZOJ 2806 [Ctsc2012]Cheat (后缀自动机+二分+单调队列+dp)
题目大意: 给你一堆模式串和文本串 对于每个文本串,我们可以把它不可重叠地拆分成很多子串,如果拆分出的串作为子串出现在了任何一个模式串中,我们称它是“眼熟的”,我们必须保证“眼熟的”子串总长度不小于文 ...
- 【BZOJ2806】[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP
[BZOJ2806][Ctsc2012]Cheat Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数接下来M行的01串,表示标准作文库接下来N行的 ...
- BZOJ2806: [Ctsc2012]Cheat(广义后缀自动机,单调队列优化Dp)
Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数接下来M行的01串,表示标准作文库接下来N行的01串,表示N篇作文 Output N行,每行一个整 ...
- [BZOJ1044][HAOI2008]木棍分割 二分 + 单调队列优化dp + 滚动数组优化dp
Description 有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长 ...
- 2018.09.26洛谷P3957 跳房子(二分+单调队列优化dp)
传送门 表示去年考普及组的时候失了智,现在看来并不是很难啊. 直接二分答案然后单调队列优化dp检验就行了. 注意入队和出队的条件. 代码: #include<bits/stdc++.h> ...
- [bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)
偷懒直接把bzoj的网页内容ctrlcv过来了 2806: [Ctsc2012]Cheat Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1943 ...
- 单调队列优化DP,多重背包
单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...
- bzoj1855: [Scoi2010]股票交易--单调队列优化DP
单调队列优化DP的模板题 不难列出DP方程: 对于买入的情况 由于dp[i][j]=max{dp[i-w-1][k]+k*Ap[i]-j*Ap[i]} AP[i]*j是固定的,在队列中维护dp[i-w ...
- hdu3401:单调队列优化dp
第一个单调队列优化dp 写了半天,最后初始化搞错了还一直wa.. 题目大意: 炒股,总共 t 天,每天可以买入na[i]股,卖出nb[i]股,价钱分别为pa[i]和pb[i],最大同时拥有p股 且一次 ...
随机推荐
- JSON对象转成formData对象,formData对象转成JSON对象
在向后端请求时,如果上传的数据里存在file文件对象,需要用到表单提交,这时候我们需要将JSON对象,转成formData对象,具体见代码 const formData = new FormData( ...
- 1.redis 安装
1.https://redis.io/download. 2. $ wget http://download.redis.io/releases/redis-3.2.9.tar.gz $ .tar.g ...
- Webpack机制、原理简单小结
一.webpack的构成 entry 代表项目的入口 module 开发中,每一个文件可以看作一个module chunk 代码块 loader 模块转化器 plugin 扩展插件,自定义w ...
- 操作系统(4)_进程同步_李善平ppt
生产者进程count++是它的临界区,消费者count--是它的临界区. 经典同步问题,死锁问题,略.
- Oracle下如何收集 Systemstate dump
2: dump (不包括lock element) 10: dump 11: dump + global cache of RAC 256: short stack (函数堆栈) 258: 256+2 ...
- python中生成器对象和return 还有循环的区别
python中生成器对象和return 还有循环的区别 在python中存在这么一个关键字yield,这个关键字在项目中经常被用到,比如我写一个函数不想它只返回一次就结束那我们就不能用return,因 ...
- music21 关联 MuseScore 和 Lilypond
在python安装 music21后,需要关联 musescore 或 lilypond 才能可以用图形化的形式看到 乐谱. 因此 在安装 music21后,需要配置环境变量,yvivid 在 mus ...
- 常见/dev/mapper/centos-root扩容
系统Centos 7 df -h 查看当前分区使用情况: dfisk /dev/xvda 对/dev/xvda磁盘进行操作(新建分区及格式化) n p 回车 默认分区号: 回车 默认磁盘创建开始位置: ...
- 模拟:HDU1034-Candy Sharing Game
解题心得: 1.直接模拟每一次分一半就行了,模拟过程,记录轮数,但是也看到有些大神使用的是链表,估计链表才是真的做法吧. 题目: Candy Sharing Game Time Limit: 2000 ...
- Hi3518EV300编译U-Boot和内核报错:loadlocale.c:130: _nl_intern_locale_data: Assertion `cnt < (sizeof (_nl_value_type_LC_TIME) / sizeof (_nl_value_type_LC_TIME[0]))' failed. Aborted (core dumped)
下载Hi3518EV300的SDK后编译内核和U-boot,发现爆出如下错误: scripts/kconfig/conf --silentoldconfig Kconfig Aborted (core ...