BZOJ 4556 [HEOI2016/TJOI2016]字符串

其实题解更多是用后缀数组+数据结构的做法,貌似也不好写。

反正才学了 sam 貌似比较简单的做法。

还是得先二分,然后倍增跳到 $ s[c...c+mid-1] $ 所在的节点,然后看看有没有 endpos 在 $ a+mid-1...b $ 内就好了。

复杂度是二分和倍增的 $ nlog^2n $。

其实这道题因为只用求 endpos 是否存在啥的 vector + lower_bound 貌似都可以过了。。但其实启发式合并也不好写,还是写一下线段树合并吧,有些时候会要求维护一些其他的信息,而且复杂度只在最初合并有区别,并不是复杂度瓶颈。

开始线段树写成 On T了半天海星。。

#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "ctime"
using namespace std;
#define MAXN 200006
int n , m , ps;
char ch[MAXN];
namespace solve { int T[MAXN*20] , ls[MAXN*20] , rs[MAXN*20] , rt[MAXN] , cn;
void add( int& rt , int l , int r , int p ) {
if( !rt ) rt = ++ cn;
++ T[rt];
if( l == r ) return;
int m = l + r >> 1;
if( p <= m ) add( ls[rt] , l , m , p );
else add( rs[rt] , m + 1 , r , p );
}
int merge( int u , int v , int l , int r ) {
if( !u || !v ) return u ^ v;
int cur = ++ cn , m = l + r >> 1;
T[cur] = T[u] + T[v];
if( l == r ) return cur;
ls[cur] = merge( ls[u] , ls[v] , l , m );
rs[cur] = merge( rs[u] , rs[v] , m + 1 , r );
return cur;
}
int que( int rt , int l , int r , int L , int R ) {
if( !rt ) return 0;
if( L <= l && R >= r ) return T[rt];
int m = l + r >> 1 , res = 0;
if( L <= m ) res += que( ls[rt] , l , m , L , R );
if( R > m ) res += que( rs[rt] , m + 1 , r , L , R );
return res;
} int son[MAXN][26] , len[MAXN] , par[MAXN] , lst[MAXN];
int last , cnt; int head[MAXN] , to[MAXN] , nex[MAXN] , ecn;
void ade( int u , int v ) {
to[++ ecn] = v , nex[ecn] = head[u] , head[u] = ecn;
}
void addall( ) {
for( int i = 2 ; i <= cnt ; ++ i ) ade( par[i] , i );
} void init( ) {
cnt = last = 1;
}
void ins( int c ) {
int cur = ++ cnt;
len[cur] = len[last] + 1;
int p = last;
while( p && !son[p][c] ) son[p][c] = cur , p = par[p];
if( !p ) par[cur] = 1;
else {
int q = son[p][c];
if( len[q] == len[p] + 1 ) par[cur] = q;
else {
int cl = ++ cnt;
memcpy( son[cl] , son[q] , sizeof son[q] );
par[cl] = par[q] , len[cl] = len[p] + 1;
par[q] = par[cur] = cl;
while( p ) { if( son[p][c] == q ) son[p][c] = cl; p = par[p]; }
}
}
last = cur , lst[++ ps] = cur;
add( rt[cur] , 1 , n , ps );
}
int G[MAXN][19];
int work( int u , int ln ) {
for( int k = 18 ; k >= 0 ; -- k )
if( len[G[u][k]] >= ln ) u = G[u][k];
return u;
}
void dfs( int u , int fa ) {
for( int i = head[u] ; i ; i = nex[i] ) {
int v = to[i];
if( v == fa ) continue;
G[v][0] = u;
for( int k = 1 ; k < 19 ; ++ k )
if( G[G[v][k-1]][k-1] ) G[v][k] = G[G[v][k-1]][k-1];
else break;
dfs( v , u );
rt[u] = merge( rt[u] , rt[v] , 1 , n );
}
}
} int main() {
//freopen("7.in","r",stdin);
//freopen("ot","w",stdout);
cin >> n >> m;
scanf("%s",ch + 1);
using namespace solve;
init( );
for( int i = 1 ; i <= n ; ++ i )
ins( ch[i] - 'a' );
addall( );
dfs( 1 , 1 );
for( int i = 1 , a , b , c , d ; i <= m ; ++ i ) {
scanf("%d%d%d%d",&a,&b,&c,&d);
int l = 1 , r = d - c + 1;
while( l <= r ) {
int mid = l + r >> 1;
int t = work( lst[c + mid - 1] , mid );
if( que( rt[t] , 1 , n , a + mid - 1 , b ) ) l = mid + 1;
else r = mid - 1;
}
printf("%d\n",r);
}
}

BZOJ 4556 [HEOI2016/TJOI2016]字符串的更多相关文章

  1. 【BZOJ 4556】[Tjoi2016&Heoi2016]字符串 SAM+二分+主席树

    这道题市面上就两种法:一种是SA+二分+主席树,一种是SAM+二分+主席树(有不少人打线段树合并???)(除此之外还有一种利用炒鸡水的数据的暴力SA,贼快.....)(当时学SA的时候没做这道题,现在 ...

  2. BZOJ.4553.[HEOI2016&TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)

    题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j) if(a[ ...

  3. P4094 [HEOI2016/TJOI2016]字符串 后缀数组+主席树+二分答案

    $ \color{#0066ff}{ 题目描述 }$ 佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物.生日礼物放在一个神奇的箱子中.箱子外边写了一个长为n的字符串s,和m个问题.佳媛姐姐必须 ...

  4. [HEOI2016/TJOI2016]字符串

    嘟嘟嘟 今天复习一下SAM. lcp固然不好做,干脆直接翻过来变成后缀.首先答案一定满足单调性,所以我们二分lcp的长度\(mid\),然后判断\(s[d \ldots d + mid - 1]\)是 ...

  5. BZOJ.4555.[HEOI2016&TJOI2016]求和(NTT 斯特林数)

    题目链接 \(Description\) 求\[\sum_{i=0}^n\sum_{j=0}^iS(i,j)\times 2^j\times j!\mod 998244353\] 其中\(S(i,j) ...

  6. [HEOI2016/TJOI2016]字符串(后缀数组+二分+主席树/后缀自动机+倍增+线段树合并)

    后缀数组解法: 先二分最长前缀长度 \(len\),然后从 \(rnk[c]\) 向左右二分 \(l\) 和 \(r\) 使 \([l,r]\) 的 \(height\geq len\),然后在主席树 ...

  7. 洛谷 P4093: bzoj 4553: [HEOI2016/TJOI2016]序列

    题目传送门:洛谷P4093. 题意简述: 给定一个长度为 \(n\) 的序列 \(a\). 同时这个序列还可能发生变化,每一种变化 \((x_i,y_i)\) 对应着 \(a_{x_i}\) 可能变成 ...

  8. BZOJ.4552.[HEOI2016/TJOI2016]排序(线段树合并/二分 线段树)

    题目链接 对于序列上每一段连续区间的数我们都可以动态开点建一棵值域线段树.初始时就是\(n\)棵. 对于每次操作,我们可以将\([l,r]\)的数分别从之前它所属的若干段区间中分离出来,合并. 对于升 ...

  9. 【[HEOI2016/TJOI2016]字符串】

    码农题啊 上来先无脑一个\(SA\)的板子,求出\(SA\)和\(het\)数组 我们只需要从\(sa[i]\in[a,b]\)的所有\(i\)中找到一个\(i\)使得\(sa[i]\)和\(rk[c ...

随机推荐

  1. 【Docker】(11)---Docker的网络概念

    一.实现原理 1.实现原理 Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为C ...

  2. [软工顶级理解组] Beta阶段事后分析

    目录 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结 质量提高 会议截图 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰 ...

  3. OO第三次博客作业--第三单元总结

    一.JML 语言的理论基础及应用工具链 JML 是一种行为接口规格语言,提供了对方法和类型的规格定义手段.通过 JML 和其支持工具,不仅可以基于规格自动构造测试用例,并整合了 SMT Solver ...

  4. 脚本:bat实现自动转换windows远程端口

    问题描述:通过一个脚本可以实现windows远程端口的转换,这个是拷贝过来学习的一个脚本 @echo off color f0 echo 修改远程桌面3389端口(支持Windows 2003 200 ...

  5. 关于评论区empty。。。

    空荡荡的毫无人烟,博主希望路过的小哥哥/小姐姐(几率较小)留下些什么--

  6. PCB中,Solder Mask与Paste Mask有啥区别呢?

    Solder Mask Layers: 即阻焊层.顾名思义,他的存在是为了防止PCB在过波峰焊的时候,不应涂锡的地方粘上锡. 可以简单理解为一个洞,该区域(洞)以外的地方,都不允许有焊锡,即只能涂绿油 ...

  7. EFCore_环境搭建与简单使用_01

    开发环境搭建 经典步骤:建实体类.建DbContext.生成数据库 本次使用codefirst模式,走下流程,(你也可以先建好数据库,用命令行的形式,直接生成DbContext,而且生成的DbCont ...

  8. VMware Workstation 16.2 Pro for Linux SLIC 2.6 & Unlocker

    请访问原文链接:https://sysin.org/blog/vmware-workstation-16-linux-slic/,查看最新版.原创作品,转载请保留出处. 作者:gc(at)sysin. ...

  9. spring-cloud-square开发实战(三种类型全覆盖)

    欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 前文<五分钟搞懂spring-clou ...

  10. 深入浅出:了解时序数据库 InfluxDB

    数据模型 1.时序数据的特征 时序数据应用场景就是在时间线上每个时间点都会从多个数据源涌入数据,按照连续时间的多种纬度产生大量数据,并按秒甚至毫秒计算的实时性写入存储. 传统的RDBMS数据库对写入的 ...