传送门


先将所有模板串扔进广义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、二分答案、单调队列的更多相关文章

  1. CTSC2012 熟悉的文章 广义后缀自动机_单调队列

    没啥难的,主要是单调队列忘了咋求了QAQ... Code: #include <cstdio> #include <algorithm> #include <cstrin ...

  2. [bzoj2806][Ctsc2012]Cheat(后缀自动机(SAM)+二分答案+单调队列优化dp)

    偷懒直接把bzoj的网页内容ctrlcv过来了 2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1943   ...

  3. bzoj 2806: [Ctsc2012]Cheat【广义SAM+二分+dp+单调队列】

    把模板串建一个广义SAM 然后在线查询,每次在SAM上预处理出一个a[i]表示i位置向前最多能匹配多长的模板串 二分答案L,dp判断,设f[i]为·~i有几个匹配,转移显然是f[i]=max{f[i- ...

  4. BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列

    BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列 题意: 分析: 拆成链,二分答案,奇偶两个单调队列维护最大子段和,记录方案. 代码: #include <cstdio&g ...

  5. Luogu4022 CTSC2012熟悉的文章(广义后缀自动机+二分答案+动态规划+单调队列)

    对作文库中的串建出广义SAM,然后显然可以二分答案,二分之后考虑暴力dp,设f[i]为前i位最长匹配长度,显然有f[i]=max(f[i-1],f[j]+i-j) (i-j>=l&&am ...

  6. [CTSC2012]熟悉的文章(广义后缀自动机+二分答案+单调队列优化DP)

    我们对作文库建出广义后缀自动机.考虑用\(SAM\)处理出来一个数组\(mx[i]\),表示从作文的第\(i\)个位置向左最远在作文库中出现的子串的长度.这个东西可以在\(SAM\)上跑\(trans ...

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

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

  8. 【BZOJ 2806】 2806: [Ctsc2012]Cheat (SAM+二分+DP+单调队列)

    2806: [Ctsc2012]Cheat Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1262  Solved: 643 Description ...

  9. loj 10181 绿色通道 二分答案+单调队列DP

    空题段长度即为单调队列长度区间 每次二分答案进行check即可 #include<bits/stdc++.h> using namespace std; ; const int inf=0 ...

随机推荐

  1. 组件化和 React

    一,对组件化的理解 1,组件的封装 -视图 -数据 -变化逻辑(数据驱动视图变化) 例: import React, { Component } from 'react'; import List f ...

  2. Salesforce自定义权限简介

    自定义权限(Custom Permission) Salesforce默认提供了多种方式设定用户的权限,比如简档.权限集等.在这些设定中,已经包括了系统中的对象.应用.字段.页面布局等组件,管理员或开 ...

  3. spring使用BeanPostProcesor实现AOP源码分析

    源码 AbstractApplicationContext#public void refresh() throws BeansException, IllegalStateException { f ...

  4. (网页)logback的使用和logback.xml详解(转)

    转自博客园:行走在云端的愚公: 一.logback的介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch.它当前分为下面下个模块: ...

  5. 用Python实现数据结构之优先级队列

    优先级队列 如果我们给每个元素都分配一个数字来标记其优先级,不妨设较小的数字具有较高的优先级,这样我们就可以在一个集合中访问优先级最高的元素并对其进行查找和删除操作了.这样,我们就引入了优先级队列 这 ...

  6. 免费ARP

    1. 免费ARP基本概念 免费ARP,也叫Gratutious ARP.无故ARP.这种ARP不同于一般的ARP请求,它的Sender IP和Target IP字段是相同的,相当于是请求自己的IP地址 ...

  7. Fragment分解使用

    Fragment碎片:作为Activity的一部分,不能单独使用: 1. Fragment特点: (1)一个Fragment可以在多个Activity中重用: (2)一个Activity内部可以嵌入多 ...

  8. C语言四舍五入

    //今天遇到了四舍五入的问题,这些问题如果不看别人的真的难想出这么巧妙的方法啊.努力积累,早日成为大佬. int i = (int)(a + 0.5) ////小数部分大于0.4,加上0.5就会超过整 ...

  9. linux内网IP如果判断出网IP地址

    [root@jumpserver ~]# curl https://ip.cn当前 IP: 162.14.210.16 来自: 河南省郑州市 xx网络

  10. July 10th, Week 29th Sunday, 2016

    Everything is good when new, but friend when old. 老朋友更醇香. When did you meet with your last friends l ...