【题解】HackerRank-Letter Islands

题目

https://www.hackerrank.com/challenges/letter-islands/problem

题意

​对于一个字符串 \(s\) 的子串 \(s'\)\(f(s')\) 的值就是把 \(s\) 中所有 \(s'\) 完整出现过的位置中 \(s'\) 的每一个字符出现过的位置全部拿出来,所得到的线段条数(如 ababaewabaq 中把 aba 出现过的位置标记出来就是 XXXXXewXXXq ,那么答案是 2)。

求满足 \(f(s')=k\)\(s'\) 的个数。

题解

  • 考虑 \(f(s')\) 到底求的是什么。就是把 \(s'\) 出现过的位置的集合拿出来,那么 \(f(s')\) 就是相邻两个位置之差不超过 \(|s'|\) 的个数 +1。
  • 接下来就好办了,后缀自动机建后缀树,树上启发式合并位置(EndPos),并维护相邻位置的差。对于每一个结点需要求相邻位置的差中的第 K 大和第 K+1 大。
  • 复杂度 \(\mathrm{O} (n\log ^2 n)\)

代码

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
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
#ifdef zerol
#define dbg(args...) do { cout << "\033[32;1m" << #args << " -> "; err(args); } while (0)
#else
#define dbg(...)
#endif
void err() { cout << "\033[39;0m" << endl; }
template<template<typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args) { for (auto x: a) cout << x << ' '; err(args...); }
template<typename T, typename... Args>
void err(T a, Args... args) { cout << a << ' '; err(args...); }
// -----------------------------------------------------------------------------
const int N = 1E5 + 100;

namespace tree {
#define mid ((l + r) >> 1)
#define lson l, mid
#define rson mid + 1, r
struct P {
int w, ls, rs;
} tr[N * 20 * 18];
int sz = 1;
int _new(int w, int ls, int rs) {
tr[sz] = {w, ls, rs};
return sz++;
}
int ins(int tt, int x, int v, int l = -1, int r = N) {
if (x < l || r < x) return tt;
const P& t = tr[tt];
if (l == r) return _new(t.w + v, 0, 0);
return _new(t.w + v, ins(t.ls, x, v, lson), ins(t.rs, x, v, rson));
}
int query(int tt, int k, int l = -1, int r = N) {
if (l == r) return l;
const P& t = tr[tt];
int w = tr[t.rs].w;
if (k <= w) return query(t.rs, k, rson);
else return query(t.ls, k - w, lson);
}
};

namespace sam {
const int M = N << 1;
int t[M][26], len[M] = {-1}, fa[M], sz = 2, last = 1, ed[M];
void ins(int ch, int pp) {
int p = last, np = last = sz++; ed[np] = pp + 1;
len[np] = len[p] + 1;
for (; p && !t[p][ch]; p = fa[p]) t[p][ch] = np;
if (!p) { fa[np] = 1; return; }
int q = t[p][ch];
if (len[p] + 1 == len[q]) fa[np] = q;
else {
int nq = sz++; len[nq] = len[p] + 1;
memcpy(t[nq], t[q], sizeof t[0]);
fa[nq] = fa[q];
fa[np] = fa[q] = nq;
for (; t[p][ch] == q; p = fa[p]) t[p][ch] = nq;
}
}

int c[M] = {1}, a[M];
void rsort() {
FOR (i, 1, sz) c[i] = 0;
FOR (i, 1, sz) c[len[i]]++;
FOR (i, 1, sz) c[i] += c[i - 1];
FOR (i, 1, sz) a[--c[len[i]]] = i;
}
}
using namespace sam;
set<int> e[M];
int d[M];

void s_ins(set<int>& e, int& d, int x) {
auto it = e.insert(x).first, l = it, r = it;
if (e.size() == 1) return;
if (l != e.begin() && r != --e.end()) {
int ll = *--l, rr = *++r;
d = tree::ins(d, rr - ll, -1);
d = tree::ins(d, x - ll, 1); d = tree::ins(d, rr - x, 1);
} else {
if (it != e.begin()) d = tree::ins(d, x - *--l, 1);
else d = tree::ins(d, *++r - x, 1);
}
}

char s[N];
int main() {
scanf("%s", s);
int K; cin >> K; --K;
FOR (i, 0, strlen(s)) ins(s[i] - 'a', i);
rsort();
LL ans = 0;

FORD (i, sz - 1, 1) {
int u = a[i], f = fa[u];
if (ed[u]) s_ins(e[u], d[u], ed[u]);
int l, r;
r = tree::query(d[u], K);
l = tree::query(d[u], K + 1);
ans += max(min(r - 1, len[u]) - max(l, len[fa[u]] + 1) + 1, 0);
if (e[f].size() < e[u].size()) {
swap(e[f], e[u]); swap(d[f], d[u]);
}
for (auto& x: e[u]) s_ins(e[f], d[f], x);

}
cout << ans << endl;
}