Luogu4022 CTSC2012 熟悉的文章 广义SAM、二分答案、单调队列
先将所有模板串扔进广义SAM。发现作文的\(L0\)具有单调性,即\(L0\)更小不会影响答案,所以二分答案。
假设当前二分的值为\(mid\),将当前的作文放到广义SAM上匹配。
设对于第\(1-i\)个字符来说,最少的失配字符数为\(dp_i\),那么\(dp_i = dp_{i-1} + 1\),且如果当前匹配长度\(len \geq mid\),还有转移\(dp_i = \min\limits_{j=i-len}^{i-mid} dp_j\)。发现在\(i\)增大的过程中\(i-len\)单调不减,因为\(i\)增大\(1\),\(len\)也最多增大\(1\)。这类似于滑动窗口问题,可以单调队列优化转移,做到复杂度\(O(LlogL)\)。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
//This code is written by Itst
using namespace std;
const int MAXN = 2.2e6 + 7;
namespace SAM{
int Lst[MAXN] , Sst[MAXN] , fa[MAXN] , trans[MAXN][2];
int cnt = 1 , L;
int insert(int p , int len , int x){
int t = ++cnt;
Lst[t] = len;
while(p && !trans[p][x]){
trans[p][x] = t;
p = fa[p];
}
if(!p){
Sst[t] = fa[t] = 1;
return t;
}
int q = trans[p][x];
Sst[t] = Lst[p] + 2;
if(Lst[q] == Lst[p] + 1){
fa[t] = q;
return t;
}
int k = ++cnt;
memcpy(trans[k] , trans[q] , sizeof(trans[k]));
Lst[k] = Lst[p] + 1;
Sst[k] = Sst[q];
Sst[q] = Lst[p] + 2;
fa[k] = fa[q];
fa[q] = fa[t] = k;
while(trans[p][x] == q){
trans[p][x] = k;
p = fa[p];
}
return t;
}
};
char s[MAXN >> 1];
int ch[MAXN >> 1][2] , dep[MAXN >> 1] , ind[MAXN >> 1];
int N , M , L , cnt = 1;
void insert(){
scanf("%s" , s + 1);
L = strlen(s + 1);
int cur = 1;
for(int i = 1 ; i <= L ; ++i){
if(!ch[cur][s[i] - '0']){
ch[cur][s[i] - '0'] = ++cnt;
dep[cnt] = dep[cur] + 1;
}
cur = ch[cur][s[i] - '0'];
}
}
void build(){
queue < int > q;
q.push(ind[1] = 1);
while(!q.empty()){
int x = q.front();
q.pop();
for(int i = 0 ; i < 2 ; ++i)
if(ch[x][i]){
ind[ch[x][i]] = SAM::insert(ind[x] , dep[ch[x][i]] , i);
q.push(ch[x][i]);
}
}
}
deque < int > q;
int dp[MAXN >> 1];
bool check(int mid){
q.clear();
int u = 1 , len = 0;
for(int i = 1 ; i <= L ; ++i){
dp[i] = dp[i - 1] + 1;
if(SAM::trans[u][s[i] - '0']){
u = SAM::trans[u][s[i] - '0'];
++len;
}
else{
while(u && !SAM::trans[u][s[i] - '0'])
u = SAM::fa[u];
!u ? (u = 1 , len = 0) : (len = SAM::Lst[u] + 1 , u = SAM::trans[u][s[i] - '0']);
}
while(!q.empty() && q.front() < i - len)
q.pop_front();
if(len >= mid){
while(!q.empty() && dp[q.back()] >= dp[i - mid])
q.pop_back();
q.push_back(i - mid);
}
if(!q.empty())
dp[i] = min(dp[i] , dp[q.front()]);
}
return dp[L] <= 0.1 * L;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in" , "r" , stdin);
//freopen("out" , "w" , stdout);
#endif
cin >> N >> M;
for(int i = 1 ; i <= M ; ++i)
insert();
build();
for(int i = 1 ; i <= N ; ++i){
scanf("%s" , s + 1);
L = strlen(s + 1);
int l = 0 , r = L;
while(l < r){
int mid = (l + r + 1) >> 1;
check(mid) ? l = mid : r = mid - 1;
}
cout << l << endl;
}
return 0;
}
Luogu4022 CTSC2012 熟悉的文章 广义SAM、二分答案、单调队列的更多相关文章
- CTSC2012 熟悉的文章 广义后缀自动机_单调队列
没啥难的,主要是单调队列忘了咋求了QAQ... Code: #include <cstdio> #include <algorithm> #include <cstrin ...
- [bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)
偷懒直接把bzoj的网页内容ctrlcv过来了 2806: [Ctsc2012]Cheat Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1943 ...
- bzoj 2806: [Ctsc2012]Cheat【广义SAM+二分+dp+单调队列】
把模板串建一个广义SAM 然后在线查询,每次在SAM上预处理出一个a[i]表示i位置向前最多能匹配多长的模板串 二分答案L,dp判断,设f[i]为·~i有几个匹配,转移显然是f[i]=max{f[i- ...
- BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列
BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列 题意: 分析: 拆成链,二分答案,奇偶两个单调队列维护最大子段和,记录方案. 代码: #include <cstdio&g ...
- Luogu4022 CTSC2012熟悉的文章(广义后缀自动机+二分答案+动态规划+单调队列)
对作文库中的串建出广义SAM,然后显然可以二分答案,二分之后考虑暴力dp,设f[i]为前i位最长匹配长度,显然有f[i]=max(f[i-1],f[j]+i-j) (i-j>=l&&am ...
- [CTSC2012]熟悉的文章(广义后缀自动机+二分答案+单调队列优化DP)
我们对作文库建出广义后缀自动机.考虑用\(SAM\)处理出来一个数组\(mx[i]\),表示从作文的第\(i\)个位置向左最远在作文库中出现的子串的长度.这个东西可以在\(SAM\)上跑\(trans ...
- Luogu-4022 [CTSC2012]熟悉的文章
广义后缀自动机+DP 对于作文库建出广义后缀自动机,广义自动机就是在每次添加一个字符串之前把\(last=0\),然后正常添加就好了 对于每个询问串,预处理出每个位置\(i\)能向前匹配的最长长度\( ...
- 【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)
2806: [Ctsc2012]Cheat Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 1262 Solved: 643 Description ...
- loj 10181 绿色通道 二分答案+单调队列DP
空题段长度即为单调队列长度区间 每次二分答案进行check即可 #include<bits/stdc++.h> using namespace std; ; const int inf=0 ...
随机推荐
- Linux 学习笔记之超详细基础linux命令 Part 9
Linux学习笔记之超详细基础linux命令 by:授客 QQ:1033553122 ---------------------------------接Part 8----------------- ...
- 13.1、多进程:进程锁Lock、信号量、事件
进程锁: 为什么要有进程锁:假如现在有一台打印机,qq要使用打印机,word文档也要使用打印机,如果没有使用进程锁,可能会导致一些问题,比如QQ的任务打印到一半,Word插进来,于是打印出来的结果是各 ...
- Scala多重继承及AOP
package traitandclass /** * Created by zhen on 2018/8/23. */ class Human { println("Human" ...
- pymongo的用法
先看一下官方给出的简单例子,涵盖了大部分内容: >>> import pymongo >>> client = pymongo.MongoClient(" ...
- nginx 了解一下
先决条件 想要流畅的配置 nginx 需要了解一下内容: 1.nginx 调用方式: 启动 (双击 exe.cmd start nginx .cmd nginx) 使用 (powershell 调用需 ...
- Linux 小知识翻译 - 「协议(protocol)」
对于理解服务器和网络来说,「协议」是不可缺少的概念. 「协议(protocol)」有「规则,规定」的意思. 实际上「协议」的函数很广,在通信领域,「协议」规定了「在通信时,什么样的情况下,以什么样的顺 ...
- January 20th, 2018 Week 3rd Saturday
We may encounter many defeats but we must not be defeated. 我们可能会失败很多次,但决不能被打败. As long as we are con ...
- Reachability
一.Reachability中介绍了取得/检测网络状态的方法. 二.使用 1.添加源文件:Reachability.h和Reachability.m 2.添加framework———SystemCon ...
- 【Beta Scrum】冲刺! 2/5
1. 今日完成情况 人员 学号 分工 是否完成 完成情况 胡武成 031502610 学习java后端登录验证方式,尝试编写登录api N 刚学会springmvc登录token拦截,准备明天登录注册 ...
- Java计模模式之六 ----- 组合模式和过滤器模式
前言 在上一篇中我们学习了结构型模式的外观模式和装饰器模式.本篇则来学习下组合模式和过滤器模式. 组合模式 简介 组合模式是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来 ...