P4022 [CTSC2012]熟悉的文章 题解
简要题意
给定 \(m\) 个模板串和 \(n\) 个匹配串,如果一个字符串是一个模板串的子串且长度不小于 \(L\) 则称其为“熟悉的”,对于每个匹配串,求一个最大的 \(L\),满足将匹配串分割,熟悉的子串的总长度大于原串长度的 \(90\%\)。
题目分析
首先对于 \(L\),如果有更大的 \(L\) 满足了它也一定满足,因此我们首先对其进行二分。
由于跟子串问题相关,且有多个模板串,我们根据模板串建出广义 SAM,分割这种方式有不可在某一位置重复的特点,因此可以考虑对其进行 DP,从前缀开始考虑,一遍 DP 一遍在 SAM 上匹配,求出当前前缀最长熟悉后缀。
设 \(f_i\) 表示割到 \(i\) 的最大熟悉长度,\(len_k\) 表示 SAM 上当前节点的最大长度,则有
\(f_i=\max(f_{i-1},f_j+(i-j)),j\in [i-len_k,i-L]\)
继续观察这个转移方程,由于我们是在 SAM 匹配,失败时往父节点走,\(i-len_k\) 递增,成功时不变,而 \(i-L\) 随 \(i\) 右移,因此决策集合只会往右增长,使用单调队列维护最优决策即可。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<deque>
#define ll long long
using namespace std;
int tot=1,nex[3000001][2],le[3000001],fa[3000001];
int dp[3000001];
string s;
struct Node{
int last,num;
};
queue<Node> q;
deque<Node> p;
void inserttrie(string s){
int p=1,len=s.length();
for(int i=0;i<len;i++){
int num=s[i]-'0';
if(!nex[p][num]) nex[p][num]=++tot;
p=nex[p][num];
}
}
int insertsam(int last,int num){
int p=last,np=nex[last][num];le[np]=le[p]+1;
p=fa[p];
for(;p && !nex[p][num];p=fa[p]) nex[p][num]=np;
if(!p) fa[np]=1;
else{
int q=nex[p][num];
if(le[q]==le[p]+1) fa[np]=q;
else{
int clone=++tot;le[clone]=le[p]+1;
for(int i=0;i<=1;i++)nex[clone][i]=(le[nex[q][i]]?nex[q][i]:0);
for(;p && nex[p][num]==q;p=fa[p]) nex[p][num]=clone;
fa[clone]=fa[q];fa[q]=fa[np]=clone;
}
}
return np;
}
void build(){
for(int i=0;i<=1;i++) if(nex[1][i]) q.push((Node){1,i});
while(!q.empty()){
int last=q.front().last,num=q.front().num;q.pop();
int cur=insertsam(last,num);
for(int i=0;i<=1;i++) if(nex[cur][i]) q.push((Node){cur,i});
}
}
bool solve(string s,int ans){
int len=s.length(),now=1,lth=0;
for(int i=1;i<=len;i++);
while(p.size()) p.pop_back();
for(int i=0;i<len;i++){
dp[i+1]=dp[i];
int num=s[i]-'0';
while(p.size() && (i+1-ans>=0 && dp[i+1-ans]-i-1+ans>p.front().last)){
p.pop_front();
}
if(i+1-ans>=0) p.push_front((Node){dp[i+1-ans]-i-1+ans,i+1-ans});
while(!nex[now][num]){
now=fa[now];
lth=le[now];
}
if(!now){now=1;lth=0;continue;}
now=nex[now][num];lth++;
while(p.size() && i+1-lth>p.back().num) p.pop_back();
if(p.size())dp[i+1]=max(dp[i+1],p.back().last+i+1);
}
double num=len*1.0/10*9;
if(num<=dp[len]) return true;
else return false;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>s;
inserttrie(s);
}
build();
for(int i=1;i<=n;i++){
cin>>s;
int l=1,r=s.length();
while(l<r){
int mid=(l+r+1)>>1;
if(solve(s,mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
P4022 [CTSC2012]熟悉的文章 题解的更多相关文章
- P4022 [CTSC2012]熟悉的文章
题目 P4022 [CTSC2012]熟悉的文章 题目大意:多个文本串,多个匹配串,我们求\(L\),\(L\)指(匹配串中\(≥L\)长度的子串出现在文本串才为"熟悉",使得匹配 ...
- [CTSC2012]熟悉的文章(后缀自动机+动态规划)
题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范 ...
- 【[CTSC2012]熟悉的文章】
题目 好题啊 \(SAM\)+单调队列优化\(dp\) 首先这个\(L\)满足单调性真是非常显然我们可以直接二分 二分之后套一个\(dp\)就好了 设\(dp[i]\)表示到达\(i\)位置熟悉的文章 ...
- [BZOJ2806][CTSC2012]熟悉的文章(Cheat)
bzoj luogu 题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么 ...
- 题解-CTSC2012 熟悉的文章
Problem bzoj 题目大意:给定多个标准串和一个文本串,全部为01串,如果一个串长度不少于\(L\)且是任意一个标准串的子串,那么它是"熟悉"的.对于文本串\(A\),把\ ...
- [CTSC2012]熟悉的文章 后缀自动机
题面:洛谷 题解: 观察到L是可二分的,因此我们二分L,然后就只需要想办法判断这个L是否可行即可. 因为要尽量使L可行,因此我们需要求出对于给定L,这个串最多能匹配上多少字符. 如果我们可以对每个位置 ...
- CTSC2012 熟悉的文章
传送门 首先很容易想到对于所有的模式串建出广义后缀自动机,之后对于我们每一个要检查的文本串,先在SAM上跑,计算出来每一个位置能匹配到的最远的位置是多少.(就是当前点减去匹配长度) 之后--考虑DP- ...
- Luogu4022 CTSC2012 熟悉的文章 广义SAM、二分答案、单调队列
传送门 先将所有模板串扔进广义SAM.发现作文的\(L0\)具有单调性,即\(L0\)更小不会影响答案,所以二分答案. 假设当前二分的值为\(mid\),将当前的作文放到广义SAM上匹配. 设对于第\ ...
- [CTSC2012]熟悉的文章 (后缀自动机 单调队列)
/* 首先答案显然是具有单调性的, 所以可以二分进行判断 然后当我们二分过后考虑dp来求最长匹配个数, 发现每个点能够转移的地点 肯定是一段区间, 然后这样就能够得到一个log^2算法 至于每个点的匹 ...
- Luogu-4022 [CTSC2012]熟悉的文章
广义后缀自动机+DP 对于作文库建出广义后缀自动机,广义自动机就是在每次添加一个字符串之前把\(last=0\),然后正常添加就好了 对于每个询问串,预处理出每个位置\(i\)能向前匹配的最长长度\( ...
随机推荐
- 三级缓存---解决 Spring 循环依赖
1. 循环依赖 1.1 什么是循环依赖 首先,什么是循环依赖?这个其实好理解,就是两个 Bean 互相依赖,类似下面这样: """ @Service public cla ...
- SpringBoot里的Servlet和实现
Servlet 接口,一个规范, SpringBoot Spring Boot 是 Spring 的子项目,正如其名字,提供 Spring 的引导( Boot )的功能. 通过 Spring Boot ...
- 【工具推荐】github打不开or加载慢?不用配置hosts,教你一键加速!
不说废话 下载watt toolkit(原名steam++) 官方地址: Watt Toolkit - 瓦特工具箱(Steam++官网) (steampp.net) 安装完后选中,点击一键加速即可. ...
- 论文解读(SimGCL)《Are Graph Augmentations Necessary? Simple Graph Contrastive Learning for Recommendation》
Note:[ wechat:Y466551 | 可加勿骚扰,付费咨询 ] 论文信息 论文标题:Are Graph Augmentations Necessary? Simple Graph Contr ...
- [刺客伍六七&黑客] 魔刀千刃
魔刀千刃的特写 诞生之日:2023.7.29 此后会在此记录如何自己写一个自己的python库以及魔刀千刃的维护过程. 魔刀千刃(evilblade) **只攻不防,天下无双** 实战 (和堆攻击帖子 ...
- 3.你不知道的go语言控制语句
目录 本篇前瞻 Leetcode习题9 题目描述 题目分析 代码编写 知识点归纳 控制结构 顺序结构(Sequence) 声明和赋值 算术运算符 位运算符 逻辑运算 分支结构 if 语句 switch ...
- LDAP:如何在windows系统下安装LDAP及连接测试
1.LDAP介绍 LDAP是一个基于X.500标准的轻量目录访问协议,与X.500不同,LDAP协议支持TCP/IP连接.全称为Lightweight Directory Access Protoco ...
- OpenSSH版本升级漏洞修复问题
Hi, I'm @Merbelue 大家好,这篇为大家介绍二进制方式对OpenSSH版本升级,在生产环境中可用于解决版本升级.漏洞修复等. @ 目录 1.环境 2.安装telnet 2.1.检查是否安 ...
- Pytorch构建超分辨率模型——常用模块
Import required libraries: import torch import torch.nn as nn import torch.optim as optim from torch ...
- mybatis数据库字段自动填充
背景描述 目前,大多数项目的数据库设计,都会添加一些公共字段,比如version(版本号).deleted(逻辑删除标识).create_time.update_time.create_by.upda ...