BZOJ 4556 [HEOI2016/TJOI2016]字符串

BZOJ 4556 [HEOI2016/TJOI2016]字符串

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

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#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);
}
}

文章作者: yijan
文章链接: https://yijan.co/old47/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog