模拟赛 T3,去医院了没打,但是感觉很好玩。
首先有一个显然的 \(O(nk^2)\),设 \(f_{i,j}\) 表示前 \(i\) 个拼出长度为 \(j\) 的最小字典序串,很遗憾的是空间和时间都存不下。
有个优化是可以对后缀跑一边背包求出 \(g_{i,j}\) 表示后缀能否拼出长度为 \(j\) 的串,只记录有用的位置。
考虑优化,对于字符串问题找找关系,思考如何确定两个串的大小关系:只要出现一位不同即可。
也就是对于两个可行的状态 \(f_{i,j},f_{i,k},j < k\),若 \(f_{i,j}\) 与 \(f_{i,k}\) 已经确定至少一位不同,就可以扔掉劣的那个状态,换句话说这两个状态同时存在一定满足 \(f_{i,j}\) 为 \(f_{i,k}\) 的前缀。
所以对于一个 \(i\) 只需要存一个串就可以了,空间降为 \(O(nk)\)。
进一步,思考刚刚得到的结论,只需要把所有不满足的状态全部扔掉即可维护当前答案。
现在需要的是快速比较两个串的字典序,这个串是由 \(i-1\) 的答案的一段前缀和 \(i\) 的整个串(或没有)拼起来,只需要快速支持求 LCP 即可。
不会 Z 函数,写的 SA,复杂度 \(O(nk \log k)\)。
#include <bits/stdc++.h>
using namespace std;
#define QwQ01AwA return 0
#define ll long long
#define debug(x) cerr << #x << " = " << x << '\n'
#define look_time cerr << 1.0 * clock() / CLOCKS_PER_SEC << '\n'
template <typename T> void ckmax(T &x, const T y) {x = max(x, y);}
template <typename T> void ckmin(T &x, const T y) {x = min(x, y);}
namespace SA {
const int N = 2e4 + 33;
int rk[N], sa[N], oldrk[N << 1], id[N], cnt[N], st[15][N], lg[N];
void build(int *w, int n) {
int m = 27;
for (int i = 0; i <= m; i++) cnt[i] = 0;
for (int i = 1; i <= n; i++) rk[i] = w[i], cnt[rk[i]]++;
for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--) sa[cnt[rk[i]]--] = i;
for (int w = 1, c = 0, tot = 0; ; w *= 2, m = c, tot = c = 0) {
for (int i = n; i > n - w; i--) id[++tot] = i;
for (int i = 1; i <= n; i++) if (sa[i] > w) id[++tot] = sa[i] - w;
for (int i = 0; i <= m; i++) cnt[i] = 0;
for (int i = 1; i <= n; i++) oldrk[i] = rk[id[i]], cnt[oldrk[i]]++;
for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--) sa[cnt[oldrk[i]]--] = id[i], oldrk[i] = rk[i];
#define cmp(u, v) (oldrk[u] == oldrk[v] && oldrk[u + w] == oldrk[v + w])
rk[sa[1]] = ++c;
for (int i = 2; i <= n; i++) {
if (!cmp(sa[i], sa[i - 1])) c++;
rk[sa[i]] = c;
}
if (c == n) break;
}
for (int i = 1, k = 0; i <= n; i++) {
if (i != 1) lg[i] = lg[i >> 1] + 1;
if (rk[i] == 1) continue;
int j = sa[rk[i] - 1];
if (k) k--;
while (max(i, j) + k <= n && w[i + k] == w[j + k]) k++;
st[0][rk[i]] = k;
}
for (int j = 1; j <= lg[n]; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
st[j][i] = min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
}
}
for (int i = 1; i <= n; i++) oldrk[i] = 0;
}
int query(int l, int r) {
// assert(l != r);
l = rk[l], r = rk[r];
if (l > r) swap(l, r);
l++;
int k = lg[r - l + 1];
return min(st[k][l], st[k][r - (1 << k) + 1]);
}
}
const int N = 2005;
const int M = 10005;
int n, k;
string s[N], t[N];
bitset <M> suf[N], f[N];
pair <int, int> stk[M]; int top;
int w[M * 2];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) cin >> s[i];
suf[n + 1].set(0);
for (int i = n; i >= 1; i--) suf[i] = suf[i + 1] | (suf[i + 1] << s[i].size());
t[1] = " " + s[1], f[1][0] = f[1][s[1].size()] = 1;
for (int i = 2; i <= n; i++) {
int len = s[i].size();
assert(len <= k);
s[i] = " " + s[i];
for (int j = 1; j < t[i - 1].size(); j++) w[j] = t[i - 1][j] - 'a' + 1;
w[t[i - 1].size()] = 27;
for (int j = 1; j <= len; j++) w[t[i - 1].size() + j] = s[i][j] - 'a' + 1;
SA::build(w, len + t[i - 1].size());
top = 0;
auto chk = [&](pair <int, int> a, pair <int, int> b) -> int {
int rev = 1, res = 0;
if (a.first > b.first) swap(a, b), rev = -1;
if (a.first == b.first) return 0;
if (!a.second) return 0;
int pa = t[i - 1].size() + 1, pb = a.first + 1;
int lcp = SA::query(pa, pb), d = min(len, b.first - a.first);
if (lcp < d) {
res = w[pa + lcp] < w[pb + lcp] ? -1 : 1;
return res * rev;
}
if (a.first + len <= b.first) return 0;
if (!b.second) return 0;
pa += d, pb = t[i - 1].size() + 1;
lcp = SA::query(pa, pb);
if (lcp < len - d) {
res = w[pa + lcp] < w[pb + lcp] ? -1 : 1;
return res * rev;
}
return 0;
};
for (int j = 0; j <= k; j++) {
if (!suf[i + 1][k - j]) continue;
pair <int, int> now = {-1, -1};
if (f[i - 1][j]) now = {j, 0};
if (j >= len && f[i - 1][j - len]) {
if (now.first == -1) now = {j - len, len};
else if (chk(now, make_pair(j - len, len)) == 1) now = {j - len, len};
}
if (now.first == -1) continue;
while (top && chk(stk[top], now) == 1) f[i][stk[top].first + stk[top].second] = 0, top--;
if (!top || !chk(stk[top], now)) f[i][j] = 1, stk[++top] = now;
}
t[i] = " ";
for (int j = 1; j <= stk[top].first; j++) t[i] += t[i - 1][j];
for (int j = 1; j <= stk[top].second; j++) t[i] += s[i][j];
}
for (int i = 1; i < t[n].size(); i++) cout << t[n][i];
QwQ01AwA;
}
标签:int,top,len,--,ARC058F,now,first
From: https://www.cnblogs.com/Anonymely/p/18412715