题目链接

简要题意

给定 \(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. shell: logging + readlog

    logging #!/bin/bash # a small tool for logging sommething # # 1. read your input # 2. save to logs f ...

  2. vulnhub_me and my grilfriend

    解题步骤 主机nmap扫描,开放22和80端口 访问该ip,发现只能本地访问 查看源代码,提示需要使用x-forwarded-for的请求头,可以在burp suite中每次添加一个请求头,也可以使用 ...

  3. K8S | Service服务发现

    服务发现与负载均衡. 一.背景 在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关.注册中心.配置中心等相关服务,可以被集群外部访问: 对于测试「Tes」环境或者 ...

  4. pc 移动端 双端切换

    实现一个项目匹配多个端,使用vue.config自带的page 实现多个页面切换.官网介绍:https://cli.vuejs.org/zh/config/#pages 在创建的vue项目中找到 vu ...

  5. 部署安装kafka集群

    准备 zookeeper节点: 172.50.13.103 172.50.13.104 172.50.13.105 kafka版本: 2.13-2.7.0 安装步骤 部署安装zookeeper集群.参 ...

  6. php批量同步数据

    php批量同步流程 首先分页获取数据 创建临时表 批量添加数据 备份原表 删除原表 修改临时表表名改为原表 代码 1 <?php 2 3 class Stock{ 4 5 private $da ...

  7. C#的重载决策

    重载是许多编程语言支持的特性.所谓重载,就是指可以定义多个名称相同但参数(个数.类型和顺序)不同的方法(函数).先来看一个例子: void Main() { char cvalue = 'a'; ma ...

  8. MyBatis-Plus批量插入方法saveBatch

    1. saveBatch能否提高插入的效率? 先说结论,saveBatch()方法也是一条一条的插入,也就是说它会产生多条insert语句,而不是一条insert语句,所以它不是真正的批量插入,更不能 ...

  9. 渗透小tis

    知己知彼,百战不殆 1.如果提示缺少参数,如{msg:params error},可尝使用字典模糊测试构造参数,进一步攻击. 2.程序溢出,int最大值为2147483647,可尝试使用该值进行整数溢 ...

  10. 带你读论文丨S&P2019 HOLMES Real-time APT Detection

    本文分享自华为云社区<[论文阅读] (09)S&P2019 HOLMES Real-time APT Detection(溯源图)>,作者: eastmount . 摘要 本文提出 ...