首页 > 其他分享 >ARC058F

ARC058F

时间:2024-09-13 18:51:03浏览次数:1  
标签:int top len -- ARC058F now first

模拟赛 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

相关文章

  • [ARC058F] Iroha Loves Strings
    题意给定\(n\)个字符串\(s_1,s_2,...,s_n\)。你需要在其中选择一些字符串,按照顺序拼接。在所有生成的长度为\(k\)的字符串中,选择字典序最小的一个。\(n\le2000,k\le10^4,\sum|s_i|\le10^6\)Sol考虑一个朴素的dp。设\(f_{i,j}\)表示前\(i\)个字......
  • ARC058F
    首先用背包算出后\(i\)个字符串能拼成的长度。考虑从前往后dp出每个长度的字典序最小的字符串。设\(f_{i,j}\)表示前\(i\)个字符串拼成的长度为\(j\)的字典序最小的字符串。显然\(f_{i,j}\)只有在\(i+1\simn\)这些字符串能拼成长度为\(k-j\)的串时才有值。注意......