题意:给定一个串集合s,每次给定一个串t,询问一个最大的L,使得存在一种划分能把t划分成若干个子串, 其中好的子串总长不小于0.9|t|。好的子串定义为长度不小于L且是s中某一个串的子串。

解:发现这个L可以二分。如果一个L满足那么小一点的L也满足。考虑如何check。

可以求最长的总好的子串长度,然后看是否大于0.9。

这样就能想到DP了。设f[i]表示t[0:i]能划分出的最长好的子串。转移就是考虑第i是否是一个好的子串的结尾。如果是就枚举卡开头,否则就是f[i - 1]。

这个每个位置都有一个最长能匹配的长度mat[i],然后我们以i结尾的好的子串长度限制就是mat[i] ~ mid。

我们发现转移过来的两个限制,右边界显然是每次 + 1的,而左边界单调递增。具体来说,如果i结尾的最长匹配是[k, i],那么i - 1结尾的最长匹配不会比[k, i - 1]还短。

所以用广义SAM搞出来mat,然后二分 + 单调队列优化DP。

注意开头的时候f[0]是mat[0] >= mid。

 #include <bits/stdc++.h>

 const int N = ;
const double eps = 1e-; int tr[N][], fail[N], len[N], tot = ;
int mat[N], f[N];
int stk[N], top, head;
char str[N]; inline int Max(const int &a, const int &b) {
return a > b ? a : b;
} inline int split(int p, int f) {
int Q = tr[p][f], nQ = ++tot;
len[nQ] = len[p] + ;
fail[nQ] = fail[Q];
fail[Q] = nQ;
memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
while(tr[p][f] == Q) {
tr[p][f] = nQ;
p = fail[p];
}
return nQ;
} inline int insert(int f, int p) {
if(tr[p][f]) {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
return Q;
}
else {
return split(p, f);
}
}
int np = ++tot;
len[np] = len[p] + ;
while(p && !tr[p][f]) {
tr[p][f] = np;
p = fail[p];
}
if(!p) {
fail[np] = ;
}
else {
int Q = tr[p][f];
if(len[Q] == len[p] + ) {
fail[np] = Q;
}
else {
fail[np] = split(p, f);
}
}
return np;
} int large[N << ];
std::bitset<N * > tag; inline void pushdown(int o) {
if(tag[o]) {
tag.set(o << );
tag.set(o << | );
large[o << ] = large[o << | ] = ;
tag.reset(o);
}
return;
} void change(int p, int v, int l, int r, int o) {
if(l == r) {
tag.reset(o);
large[o] = v;
return;
}
pushdown(o);
int mid = (l + r) >> ;
if(p <= mid) change(p, v, l, mid, o << );
else change(p, v, mid + , r, o << | );
large[o] = Max(large[o << ], large[o << | ]);
return;
} int getMax(int L, int R, int l, int r, int o) {
if(L <= l && r <= R) {
return large[o];
}
int mid = (l + r) >> , ans = ;
pushdown(o);
if(L <= mid) ans = getMax(L, R, l, mid, o << );
if(mid < R) ans = Max(ans, getMax(L, R, mid + , r, o << | ));
return ans;
} int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = ; i <= m; i++) {
scanf("%s", str);
int len = strlen(str), last = ;
for(int j = ; j < len; j++) {
last = insert(str[j] - '', last);
}
memset(str, , len * sizeof(char));
} for(int A = ; A <= n; A++) {
scanf("%s", str);
int Len = strlen(str); int p = , lenth = ;
for(int i = ; i < Len; i++) {
int f = str[i] - '';
while(p && !tr[p][f]) {
p = fail[p];
lenth = len[p];
}
if(tr[p][f]) {
p = tr[p][f];
lenth++;
}
else {
p = , lenth = ;
}
mat[i] = lenth;
//printf("mat %d = %d \n", i, mat[i]);
} int l = , r = Len;
while(l < r) {
int mid = (l + r + ) >> ; f[] = (mat[] >= mid); /// ERROR : f[0] = mat[0]
stk[head = top = ] = ;
for(int i = ; i < Len; i++) {
f[i] = f[i - ];
int L = i - mat[i], R = i - mid;
while(head <= top && f[R] - R >= f[stk[top]] - stk[top]) {
--top;
}
stk[++top] = R;
while(head < top && stk[head] < L) {
++head;
}
if(L <= stk[head] && stk[head] <= R) {
f[i] = Max(f[i], f[stk[head]] + i - stk[head]);
}
}
//printf("mid = %d f = %d \n", mid, f[Len - 1]);
if( * f[Len - ] >= * Len) {
l = mid;
}
else {
r = mid - ;
}
}
printf("%d\n", r);
memset(str, , Len * sizeof(char));
} return ;
}

AC代码

我非常傻,一开始写的是个线段树,没看出来单调性...

洛谷P4022 熟悉的文章的更多相关文章

  1. P4022 [CTSC2012]熟悉的文章

    题目 P4022 [CTSC2012]熟悉的文章 题目大意:多个文本串,多个匹配串,我们求\(L\),\(L\)指(匹配串中\(≥L\)长度的子串出现在文本串才为"熟悉",使得匹配 ...

  2. [洛谷OJ] P1114 “非常男女”计划

    洛谷1114 “非常男女”计划 本题地址:http://www.luogu.org/problem/show?pid=1114 题目描述 近来,初一年的XXX小朋友致力于研究班上同学的配对问题(别想太 ...

  3. 洛谷P2516 [HAOI2010]最长公共子序列(LCS,最短路)

    洛谷题目传送门 一进来就看到一个多月前秒了此题的ysn和YCB%%% 最长公共子序列的\(O(n^2)\)的求解,Dalao们想必都很熟悉了吧!不过蒟蒻突然发现,用网格图貌似可以很轻松地理解这个东东? ...

  4. 洛谷P1516 青蛙的约会(扩展欧几里德)

    洛谷题目传送门 很容易想到,如果他们相遇,他们初始的位置坐标之差\(x-y\)和跳的距离\((n-m)t\)(设\(t\)为跳的次数)之差应该是模纬线长\(l\)同余的,即\((n-m)t\equiv ...

  5. 洛谷 P2317 [HNOI2005]星际贸易 解题报告

    P2317 [HNOI2005]星际贸易 题目描述 输入输出格式 输入格式: 输出格式: 如果可以找到这样的方案,那么输出文件output.txt中包含两个整数X和Y.X表示贸易额,Y表示净利润并且两 ...

  6. 洛谷P3348 [ZJOI2016]大森林(LCT,虚点,树上差分)

    洛谷题目传送门 思路分析 最简单粗暴的想法,肯定是大力LCT,每个树都来一遍link之类的操作啦(T飞就不说了) 考虑如何优化算法.如果没有1操作,肯定每个树都长一样.有了1操作,就来仔细分析一下对不 ...

  7. 洛谷 P1045 【麦森数】快速幂

    不用快速幂,压位出奇迹! 本人是个蒟蒻,不太熟悉快速幂,这里给大家介绍一种压位大法. 让我们来分析一下题目,第一位是送分的,有一个专门求位数的函数:n*log10(2)+1. 然后题目中p<=3 ...

  8. 洛谷 P1306 斐波那契公约数

    洛谷 P1306 斐波那契公约数 题目描述 对于Fibonacci数列:1,1,2,3,5,8,13......大家应该很熟悉吧~~~但是现在有一个很“简单”问题:第n项和第m项的最大公约数是多少? ...

  9. 洛谷P1102 A-B数对

    洛谷P1102 A-B数对 https://www.luogu.org/problem/show?pid=1102 题目描述 出题是一件痛苦的事情! 题目看多了也有审美疲劳,于是我舍弃了大家所熟悉的A ...

随机推荐

  1. 课程笔记-lisanke

    1.判断真需求假需求 真需求:所有人都需要的功能 假需求:只有自己需要的功能 2.找到目标用户 ①不要直接询问是否需要这个功能 ②旁敲侧击式提问:用户使用了什么方式?之前都是怎么做的? case:购物 ...

  2. 注解@Qualifier@Primary

    在Controller中需要注入service那么我的这个server有两个实现类如何区分开这两个impl呢 根据注入资源的注解不同实现的方式有一点小小的区别 在Controller中使用 @Auto ...

  3. 2019-7-2-asp-dotnet-core-通过图片统计-csdn-用户访问

    title author date CreateTime categories asp dotnet core 通过图片统计 csdn 用户访问 lindexi 2019-7-2 19:21:2 +0 ...

  4. centos6 nginx安装好以后,添加拓展ssl

    前言 安装nginx的时候,只是执行最简单的安装,--user=nobody --group=nobody --prefix=/usr/local/nginx_1.8.1,没有安装http_ssl_m ...

  5. c语言学习笔记 - 二进制文件

    在进行文件操作的时候,有时候是用文本的形式存在文件里面,例如用 fprintf(fp,"%d",123) 存一个数据123,实际的存储是已1,2,3这3个ASCII码存入,打开文件 ...

  6. Android基础控件ScrollView滚动条的使用

    1.简介 ScrollView是一个FrameLayout的容器,不过在他的基础上添加了滚动,允许显示的比实际多的内容!另外,只能够往里面放置一个子元素,可以是单一的组件,又或者一个布局包裹着的复杂的 ...

  7. 《DSP using MATLAB》Problem 8.19

    代码: %% ------------------------------------------------------------------------ %% Output Info about ...

  8. VRRP概述-转

    本文介绍了VRRP的基本原理.特点和应用. VRRP概述 随着Internet的发展,人们对网络的可靠性的要求越来越高.对于局域网用户来说,能够时刻与外部网络保持联系是非常重要的. 通常情况下,内部网 ...

  9. c#设置文件及文件夹的属性

    c#中通过FileAttributes枚举来设置文件或文件夹的属性. FileAttributes 枚举 成员名称 说明 Archive 文件的存档状态.应用程序使用此属性为文件加上备份或移除标记. ...

  10. PHP CURL 异步测试

    需求, 请求第三方接口获取数据, 单个接口0.1秒, 如果有10万个接口, 那么岂不是得1万秒才能请求完, 所以使用PHP异步测试一下, 其他的方法还有: 1.使用队列, SupserVior 开多个 ...