LibreOJ 3108 「TJOI2019」甲苯先生和大中锋的字符串

就一大水题(

出现 \(k\) 次,考虑截取后缀数组上的一个长度为 \(k\) 的区间,求得它们的 LCP,那么 LCP 以内的长度是出现次数至少 \(k\) 次的。
但是要确保恰好 \(k\) 次的话,可以考虑看一下区间开头和结尾的 height 值,便可得到这一段区间的贡献。
差分统计即可。
(ST 表在洛谷被卡常,被迫换了单调队列)

代码:

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
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5;
const int LG = 17;
int T,n,k;
char a[N + 5];
int sa[N + 5],rk[(N << 1) + 5],tpr[(N << 1) + 5],c[N + 5],height[N + 5];
int q[N + 5],head,tail;
int sum[N + 5],ans;
void build_SA()
{
memset(sa,0,sizeof sa),memset(rk,0,sizeof rk),memset(tpr,0,sizeof tpr),memset(height,0,sizeof height);
int m = 127;
memset(c,0,sizeof c);
for(register int i = 1;i <= n;++i)
++c[rk[i] = a[i]];
for(register int i = 1;i <= m;++i)
c[i] += c[i - 1];
for(register int i = n;i;--i)
sa[c[rk[i]]--] = i;
for(register int w = 1,p = 0;p < n;w <<= 1,m = p)
{
p = 0;
for(register int i = 1;i <= w;++i)
tpr[++p] = n - w + i;
for(register int i = 1;i <= n;++i)
if(sa[i] > w)
tpr[++p] = sa[i] - w;
memset(c,0,sizeof c);
for(register int i = 1;i <= n;++i)
++c[rk[i]];
for(register int i = 1;i <= m;++i)
c[i] += c[i - 1];
for(register int i = n;i;--i)
sa[c[rk[tpr[i]]]--] = tpr[i];
swap(rk,tpr),rk[sa[1]] = p = 1;
for(register int i = 2;i <= n;++i)
rk[sa[i]] = tpr[sa[i]] == tpr[sa[i - 1]] && tpr[sa[i] + w] == tpr[sa[i - 1] + w] ? p : ++p;
}
for(register int i = 1,k = 0;i <= n;++i)
{
k -= (bool)k;
for(;a[i + k] == a[sa[rk[i] - 1] + k];++k);
height[rk[i]] = k;
}
}
int main()
{
scanf("%d",&T);
for(;T;--T)
{
memset(sum,0,sizeof sum),memset(a,0,sizeof a);
scanf("%s%d",a + 1,&k);
n = strlen(a + 1),sum[ans = n + 1] = 1;
build_SA(),head = 1,tail = 0;
for(register int i = 2;i <= k;++i)
{
for(;head <= tail && height[q[tail]] >= height[i];--tail);
q[++tail] = i;
}
for(register int l = 1,r = k,L,R;r <= n;++l,++r)
{
for(;head <= tail && q[head] <= l;++head);
for(;head <= tail && height[q[tail]] >= height[r];--tail);
q[++tail] = r;
L = max(height[l],height[r + 1]) + 1,R = k == 1 ? n - sa[l] + 1 : height[q[head]];
if(L <= R)
++sum[L],--sum[R + 1];
}
for(register int i = 1;i <= n;++i)
sum[i] += sum[i - 1],sum[i] >= sum[ans] && (ans = i);
printf("%d\n",ans == n + 1 ? -1 : ans);
}
}