题目链接

首先二分答案L。然后就是判断能否将原串划分出一些长度不小于L的子串,这些子串要是给定n个串中的某个串的子串,且满足它们的长度之和不小于原串长度的90%。

贪心多长选一段什么的显然不对。老老实实DP。

设\(f[i]\)为到\(i\)划分出的最长长度(不用想什么奇奇怪怪的状态啊→_→),则\(f[i]=\max\{f[i-1],\ f[j]+i-j\}\) (\(s[i\sim j]\)是\(n\)个串中某串的子串,且\(i-j\geq L\))。

求以某位置结尾的子串是否匹配,可以对\(n\)个串建广义SAM,原串在上面匹配就能得到每个位置作为后缀所能匹配的最大长度,记为\(mx[i]\)。

那么\(j\)的范围就是\(i-mx[i]\leq j\leq i-L\).

这是\(n^2\)的,要优化。因为贪心不对,区间内的数还是要都尝试更新一遍的。观察决策位置是否有单调性,比如\(i\)与\(i+1\),有\(mx[i]+1\geq mx[i+1]\)。

\[mx[i]+1\geq mx[i+1]\\i-mx[i]\leq i+1-mx[i+1]$$ (凑出个$i+1$)
即决策位置是单调不降的。只需用单调队列维护当前区间$f[j]-j$的最值就可以了。

另外卡精度,0.9*n会偏大?要减个eps。(不想再看浮点数怎么存储了...太sxbk了吧)

顺便题目描述挺有趣233

```cpp
//63128kb 820ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define eps 1e-8
const int N=2200007;//字节。。

int n,m;
struct Suffix_Automaton
{
int tot,las,son[N][2],fa[N],len[N],mx[N],q[N],f[N];
char s[N];

Suffix_Automaton() {tot=las=1;}
void Insert(int c)
{
int p=las,np=++tot; len[las=np]=len[p]+1;
for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
if(!p) fa[np]=1;
else
{
int q=son[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot; len[nq]=len[p]+1;
memcpy(son[nq],son[q],sizeof son[q]);
fa[nq]=fa[q], fa[q]=fa[np]=nq;
for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
}
}
}
void Build()
{
scanf("%s",s), las=1;//!
for(int i=0,l=strlen(s); i<l; ++i) Insert(s[i]-'0');
}
void Get_mx(char *s)
{
for(int c,now=0,p=1,i=1,l=strlen(s+1); i<=l; mx[i++]=now)
if(son[p][c=s[i]-'0']) ++now, p=son[p][c];
else
{
for(; p&&!son[p][c]; p=fa[p]);
if(!p) now=0, p=1;
else now=len[p]+1, p=son[p][c];
}
}
bool Check(int L,int n)
{
int h=1,t=0; f[0]=0;
for(int i=1; i<=n; ++i)
{
f[i]=f[i-1];
if(i>=L && L<=mx[i])
{
int p=i-L;
while(h<=t && f[q[t]]-q[t]<=f[p]-p) --t;
q[++t]=p;
}
while(h<=t && q[h]<i-mx[i]) ++h;
if(h<=t) f[i]=std::max(f[i],i+f[q[h]]-q[h]);//好像f[0]=INF不太方便
}
return (double)f[n]>=0.9*n-eps;//0.89999999
}
void Query()
{
scanf("%s",s+1), Get_mx(s);
int len=strlen(s+1);
int l=1,r=len,mid,ans=0;
while(l<=r)
if(Check(mid=l+r>>1,len)) ans=mid,l=mid+1;
else r=mid-1;
printf("%d\n",ans);
}
}sam;

int main()
{
scanf("%d%d",&n,&m);
while(m--) sam.Build();
while(n--) sam.Query();
return 0;
}
```\]

BZOJ.2806.[CTSC2012]Cheat(广义后缀自动机 DP 单调队列)的更多相关文章

  1. BZOJ 2806: [Ctsc2012]Cheat [广义后缀自动机 单调队列优化DP 二分]

    2806: [Ctsc2012]Cheat 题意: 多个主串和多个询问串,每次询问将询问串分成多个连续子串,如果一个子串长度>=L且在主串中出现过就是熟悉的 如果熟悉的字符串长度>=询问串 ...

  2. bzoj 2806 [Ctsc2012]Cheat——广义后缀自动机+单调队列优化DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2806 只想着怎么用后缀数据结构做,其实应该考虑结合其他算法. 可以二分那个长度 L .设当前 ...

  3. 【BZOJ2806】[Ctsc2012]Cheat 广义后缀自动机+二分+单调队列优化DP

    [BZOJ2806][Ctsc2012]Cheat Description Input 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库的行数接下来M行的01串,表示标准作文库接下来N行的 ...

  4. 【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)

    2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1262  Solved: 643 Description ...

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

    题目 输入格式 第一行两个整数N,M表示待检查的作文数量,和小强的标准作文库 的行数 接下来M行的01串,表示标准作文库 接下来N行的01串,表示N篇作文 输出格式 N行,每行一个整数,表示这篇作文的 ...

  6. BZOJ 2806 Luogu P4022 [CTSC2012]Cheat (广义后缀自动机、DP、二分、单调队列)

    题目链接: (bzoj) https://www.lydsy.com/JudgeOnline/problem.php?id=2806 (luogu) https://www.luogu.org/pro ...

  7. BZOJ2806: [Ctsc2012]Cheat(广义后缀自动机,单调队列优化Dp)

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

  8. CTSC2012 熟悉的文章 广义后缀自动机_单调队列

    没啥难的,主要是单调队列忘了咋求了QAQ... Code: #include <cstdio> #include <algorithm> #include <cstrin ...

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

    2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 583  Solved: 330[Submit][Statu ...

随机推荐

  1. POJ-1459 Power Network(最大流)

    https://vjudge.net/problem/POJ-1459 题解转载自:優YoU http://user.qzone.qq.com/289065406/blog/1299339754 解题 ...

  2. Spring RedisTemplate操作-String操作(2)

    @Autowired @Resource(name="redisTemplate") private RedisTemplate<String, String> rt; ...

  3. HDU 1241 Oil Deposits DFS搜索题

    题目大意:给你一个m*n的矩阵,里面有两种符号,一种是 @ 表示这个位置有油田,另一种是 * 表示这个位置没有油田,现在规定相邻的任意块油田只算一块油田,这里的相邻包括上下左右以及斜的的四个方向相邻的 ...

  4. Spark笔记之累加器(Accumulator)

    一.累加器简介 在Spark中如果想在Task计算的时候统计某些事件的数量,使用filter/reduce也可以,但是使用累加器是一种更方便的方式,累加器一个比较经典的应用场景是用来在Spark St ...

  5. C++ socket 网络编程 简单聊天室

    操作系统里的进程通讯方式有6种:(有名/匿名)管道.信号.消息队列.信号量.内存(最快).套接字(最常用),这里我们来介绍用socket来实现进程通讯. 1.简单实现一个单向发送与接收 这是套接字的工 ...

  6. 【干货】已Window7 系统为例,谈谈boot引导程序-------附带看看数据隐藏

    来源:Unit 3: Unix/Linux File System 3.1 Unix/Linux File System Booting Process 使用工具:EnCase Forensic 学习 ...

  7. 终极利器!利用appium和mitmproxy登录获取cookies

    环境搭建 参考我之前写的https://www.cnblogs.com/c-x-a/p/9163221.html appium 代码start_appium.py # -*- coding: utf- ...

  8. WPF使用DataGridComboBoxColumn完成绑定

    在使用DataGrid的时候,有时候需要使某些列为ComboBox,这时自然想到使用DataGridComboBoxColumn,但是如果使用的是ItemsSource数据绑定后台的对象,就会发现,这 ...

  9. 【转】深入理解C++中public、protected及private用法

    首先明白以下两点: 1.类的一个特征就是封装,public和private作用就是实现这一目的. 即:用户代码(类外)可以访问public成员而不能访问private成员:private成员只能由类成 ...

  10. Mysql读写分离-Amoeba Proxy

    参考:http://www.linuxidc.com/Linux/2015-10/124115.htm 一个完整的MySQL读写分离环境包括以下几个部分: 应用程序client database pr ...