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. NKOJ-4573 Falsita

    问题描述: 到海边了呢...... 如果没有那次选择,现在是不是会好些呢...... 都过去了. 仰望着星空,迎面吹过一阵阵海风,倚靠着护栏,Fine 在海边静静地伫立着,在一个个无际的长夜后,Fin ...

  2. DP秒思维

    DP算法对于大部分题有着良好的能力,但有些题目我们要转换思维,不能直接的设具体的转态.... 最近做了两道秒题,在这里分享一下: https://ac.nowcoder.com/acm/contest ...

  3. Luogu P1850 [NOIp2016提高组]换教室 | 期望dp

    题目链接 思路: <1>概率与期望期望=情况①的值*情况①的概率+情况②的值*情况②的概率+--+情况n的值*情况n的概率举个例子,抛一个骰子,每一面朝上的概率都是1/6,则这一个骰子落地 ...

  4. 这一次,解决Flutter Dialog的各种痛点!

    前言 Q:你一生中闻过最臭的东西,是什么? A:我那早已腐烂的梦. 兄弟萌!!!我又来了! 这次,我能自信的对大家说:我终于给大家带了一个,能真正帮助大家解决诸多坑比场景的pub包! 将之前的flut ...

  5. virt-v2v命令将ESXI 虚机迁移到OpenStack中

    一简介: virt-v2v是将外部的虚拟化平台上的虚拟机转化到可以运行的KVM平台上.它可以读取在VMware.Xen运行Hyper-V和其他虚拟机管理程序上的Windows和Linux的虚拟机,并将 ...

  6. svn与git区别

    代码扫描工具介绍:https://baijiahao.baidu.com/s?id=1629218655164599200&wfr=spider&for=pc Git和SVN的区别与联 ...

  7. 【java+selenium3】模态框处理(五)

    一.模态框的定义: 模态对话框(Modal Dialogue Box , 又叫做模式对话框),是指在用户想要对话框以外的应用程序进行操作时候,必须先对该对话框进行响应.如单击[确定]或者[返回]按钮等 ...

  8. React 三大属性state,props,refs以及组件嵌套的应用

    React 三大属性state,props,refs以及组件嵌套的应用 该项目实现了一个简单的表单输入添加列表的内容 代码如下 <!DOCTYPE html> <html> & ...

  9. 用Python实现人像动漫化

    用Python实现人像动漫化 本文章会教你如何使用Python实现人像动漫化,先看看效果实例: 实现过程如下: 本案例是使用百度的API来实现的,首先需要进入百度AI开放平台注册账号,具体流程就不讲了 ...

  10. 1. 处理静态资源 2. controller如何接受请求得参数 3. 如何把controller得数据保存到view. 4. 在controller如何完成重定向到指定路径 5. controller返回json数据

    1. 1. 处理静态资源2. controller如何接受请求得参数3. 如何把controller得数据保存到view.4. 在controller如何完成重定向到指定路径5. controller ...