yaoxi-std 的博客

$\text{开}\mathop{\text{卷}}\limits^{ju\check{a}n}\text{有益}$

0%

P6095 [JSOI2015] 串分割

P6095 [JSOI2015] 串分割

题面

题目链接

解法

显然分割的串长度不是$\lceil \frac{n}{k} \rceil$就是$\lfloor \frac{n}{k} \rfloor$。

不妨先破环为链,建立后缀数组并二分最大值后缀的排名$mx$,判断是否能将$n$个数划分成$k$段使得每段都$\le mx$。

首先枚举起始位置,设$len=\lceil \frac{n}{k} \rceil$,则一共最多有$len$个起始位置,然后每次贪心地划分,假设当前从$cur$位置开始划分,如果$s[cur…cur+len-1]\le mx$则划分$len$个,否则划分$len-1$个。重复$k$次看划分的总长度是否$\ge n$。判断的时间复杂度为$O(len \times \frac{n}{len})=O(n)$。

思考这种贪心的正确性。如果前一次划分$len$个,后一次由于$\gt mx$导致只划分了$len-1$个,总共就划分了$2\times len-1$个,容易发现这样和先划分$len-1$个再划分$len$个是一样的,所以正确性显然。故此问题能够在$O(n\log n)$的时间复杂度内解决。

AC代码

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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
* @file: P6095.cpp
* @author: yaoxi-std
* @url: https://www.luogu.com.cn/problem/P6095
*/
// #pragma GCC optimize ("O2")
// #pragma GCC optimize ("Ofast", "inline", "-ffast-math")
// #pragma GCC target ("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define resetIO(x) \
freopen(#x ".in", "r", stdin), freopen(#x ".out", "w", stdout)
#define debug(fmt, ...) \
fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__)
template <class _Tp>
inline _Tp& read(_Tp& x) {
bool sign = false;
char ch = getchar();
long double tmp = 1;
for (; !isdigit(ch); ch = getchar())
sign |= (ch == '-');
for (x = 0; isdigit(ch); ch = getchar())
x = x * 10 + (ch ^ 48);
if (ch == '.')
for (ch = getchar(); isdigit(ch); ch = getchar())
tmp /= 10.0, x += tmp * (ch ^ 48);
return sign ? (x = -x) : x;
}
template <class _Tp>
inline void write(_Tp x) {
if (x < 0)
putchar('-'), x = -x;
if (x > 9)
write(x / 10);
putchar((x % 10) ^ 48);
}
const int MAXN = 6e5 + 10;
const int LOGN = 19;
const int INF = 0x3f3f3f3f3f3f3f3f;
struct SuffixArray {
int n, sa[MAXN], rk[MAXN], tp[MAXN], he[MAXN];
void radix_sort(int m) {
static int buc[MAXN];
for (int i = 0; i <= m; ++i)
buc[i] = 0;
for (int i = 1; i <= n; ++i)
buc[rk[i]]++;
for (int i = 1; i <= m; ++i)
buc[i] += buc[i - 1];
for (int i = n; i >= 1; --i)
sa[buc[rk[tp[i]]]--] = tp[i];
}
void init(int n, char* s) {
this->n = n;
int m = 200;
for (int i = 1; i <= n; ++i)
rk[i] = s[i] + 1, tp[i] = i;
radix_sort(m);
for (int w = 1, p = 0; p < n; m = p, w <<= 1) {
p = 0;
for (int i = 1; i <= w; ++i)
tp[++p] = n - w + i;
for (int i = 1; i <= n; ++i)
if (sa[i] > w)
tp[++p] = sa[i] - w;
radix_sort(m);
copy(rk + 1, rk + n + 1, tp + 1);
rk[sa[1]] = p = 1;
for (int i = 2; i <= n; ++i) {
if (tp[sa[i - 1]] == tp[sa[i]] && tp[sa[i - 1] + w] == tp[sa[i] + w])
rk[sa[i]] = p;
else
rk[sa[i]] = ++p;
}
}
for (int i = 1, k = 0; i <= n; ++i) {
if (k)
k--;
while (s[i + k] == s[sa[rk[i] - 1] + k])
k++;
he[rk[i]] = k;
}
}
} sa;
int n, k, ln;
int lg2[MAXN], st[MAXN][LOGN];
char str[MAXN];
int getlcp(int l, int r) {
if(l > r)
swap(l, r);
if (l == r)
return INF;
int k = lg2[r - l];
return min(st[l + 1][k], st[r - (1 << k) + 1][k]);
}
bool comp_lesseq(int x, int y, int len) {
int lcp = getlcp(x, y);
if (lcp >= len)
return true;
return str[sa.sa[x] + lcp] <= str[sa.sa[y] + lcp];
}
bool check(int mx) {
for (int st = 1; st <= ln; ++st) {
int cur = st;
for (int i = 1; i <= k; ++i) {
if (comp_lesseq(sa.rk[cur], mx, ln))
cur += ln;
else
cur += ln - 1;
}
if (cur >= st + n)
return true;
}
return false;
}
signed main() {
read(n), read(k);
scanf("%s", str + 1);
copy(str + 1, str + n + 1, str + n + 1);
sa.init(n + n, str);
for (int i = 2; i <= n + n; ++i)
lg2[i] = (i & (i - 1)) ? lg2[i - 1] : lg2[i - 1] + 1;
for (int i = 1; i <= n + n; ++i)
st[i][0] = sa.he[i];
for (int j = 1; j < LOGN; ++j)
for (int i = 1; i + (1 << j) - 1 <= n + n; ++i)
st[i][j] = min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
ln = (n - 1) / k + 1;
int l = 1, r = n + n, ans = r;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid))
r = mid - 1, ans = mid;
else
l = mid + 1;
}
for (int i = 0; i < ln; ++i)
putchar(str[sa.sa[ans] + i]);
putchar('\n');
return 0;
}