题目链接

简要题意

给定 \(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]熟悉的文章 题解的更多相关文章

  1. P4022 [CTSC2012]熟悉的文章

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

  2. [CTSC2012]熟悉的文章(后缀自动机+动态规划)

    题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么觉得熟悉,仿佛是某些范 ...

  3. 【[CTSC2012]熟悉的文章】

    题目 好题啊 \(SAM\)+单调队列优化\(dp\) 首先这个\(L\)满足单调性真是非常显然我们可以直接二分 二分之后套一个\(dp\)就好了 设\(dp[i]\)表示到达\(i\)位置熟悉的文章 ...

  4. [BZOJ2806][CTSC2012]熟悉的文章(Cheat)

    bzoj luogu 题目描述 阿米巴是小强的好朋友. 在小强眼中,阿米巴是一个作文成绩很高的文艺青年.为了获取考试作文的真谛,小强向阿米巴求教.阿米巴给小强展示了几篇作文,小强觉得这些文章怎么看怎么 ...

  5. 题解-CTSC2012 熟悉的文章

    Problem bzoj 题目大意:给定多个标准串和一个文本串,全部为01串,如果一个串长度不少于\(L\)且是任意一个标准串的子串,那么它是"熟悉"的.对于文本串\(A\),把\ ...

  6. [CTSC2012]熟悉的文章 后缀自动机

    题面:洛谷 题解: 观察到L是可二分的,因此我们二分L,然后就只需要想办法判断这个L是否可行即可. 因为要尽量使L可行,因此我们需要求出对于给定L,这个串最多能匹配上多少字符. 如果我们可以对每个位置 ...

  7. CTSC2012 熟悉的文章

    传送门 首先很容易想到对于所有的模式串建出广义后缀自动机,之后对于我们每一个要检查的文本串,先在SAM上跑,计算出来每一个位置能匹配到的最远的位置是多少.(就是当前点减去匹配长度) 之后--考虑DP- ...

  8. Luogu4022 CTSC2012 熟悉的文章 广义SAM、二分答案、单调队列

    传送门 先将所有模板串扔进广义SAM.发现作文的\(L0\)具有单调性,即\(L0\)更小不会影响答案,所以二分答案. 假设当前二分的值为\(mid\),将当前的作文放到广义SAM上匹配. 设对于第\ ...

  9. [CTSC2012]熟悉的文章 (后缀自动机 单调队列)

    /* 首先答案显然是具有单调性的, 所以可以二分进行判断 然后当我们二分过后考虑dp来求最长匹配个数, 发现每个点能够转移的地点 肯定是一段区间, 然后这样就能够得到一个log^2算法 至于每个点的匹 ...

  10. Luogu-4022 [CTSC2012]熟悉的文章

    广义后缀自动机+DP 对于作文库建出广义后缀自动机,广义自动机就是在每次添加一个字符串之前把\(last=0\),然后正常添加就好了 对于每个询问串,预处理出每个位置\(i\)能向前匹配的最长长度\( ...

随机推荐

  1. Button按钮:得到鼠标焦点后自动放大,失去鼠标焦点后自动缩小_

    作用 程序设计过程中,我们经常需要增加一些动态效果,以此改善用户的使用体验.本文将介绍一种方法,动态显示按钮状态,使其得到鼠标焦点后自动放大,失去鼠标焦点后自动缩小. 效果图 先放一张原图(鼠标还未移 ...

  2. Day03_Java_作业

    A:选择题 1.给出以下代码,请问表达式grade==70在以下两条语句中是否获得执行? boolean flag = false && grade == 70;//第一条语句 boo ...

  3. Mybatis(日志工厂)

    日志工厂 如果一个数据库操作,出现了异常,我们需要排错.所以日志就是最好的助手 曾经:sout.debug 现在:日志工厂 SLF4J[工作中,springboot] LOG4J[掌握] LOG4J2 ...

  4. Shell 摘抄:growpart中的参数处理

    下面这段代码中,变量cur表示这次循环所要处理的参数.如果没有触发前面的选项开关,第一个参数会被赋值给$DISK,第二个参数会赋值给$PART. 强无敌!- while [ $# -ne 0 ]; d ...

  5. test20230225考试总结(2023春 · 图论)

    前言 I hate questions that already exist!! 我讨厌原题!! 赛时得分明细: A B C D E F Total Rank 100 100 10 56 0 44 3 ...

  6. redis的一些简单操作(针对key)

    redis默认16个数据,默认使用0号 select为切换数据库的关键字 select 1  切换数据库 设置值 set k1 lucy       key为 k1  value 为 lucy 查看全 ...

  7. 快速入门OpenCv(python版)

    OpenCV是一个(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows和Mac OS操作系统上.它轻量级而且高效--由一系列 C 函数和少量 C++ 类构成,同时提供了Python. ...

  8. 论文解读(AAD)《Knowledge distillation for BERT unsupervised domain adaptation》

    Note:[ wechat:Y466551 | 可加勿骚扰,付费咨询 ] 论文信息 论文标题:Knowledge distillation for BERT unsupervised domain a ...

  9. 搭建rsyncd服务

    前言 rsync常用来做文件传输和同步.本文示例中客户端通过rsync同步服务端的/home/tmp目录到本地(不是将客户端的文件同步到服务端). 环境信息 IP 系统版本 rsync版本 说明 19 ...

  10. 前后端分离中台框架 Admin.Core 学习-介绍与配置说明

    中台框架后端项目 Admin.Core 的介绍与配置说明 中台admin是前后端分离权限管理系统,Admin.Core为后端项目,基于.NET 7.0开发. 支持多租户.数据权限.动态 Api.任务调 ...