把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

[ARC081E] Don't Be a Subsequence 题目分析

题目概述

求所有不是 A 的子序列的最短小写英文字母字符串中字典序最小的一个。

分析

一个类似于 CSP2025-S 中第三题的动态规划。

倒着做。

\(f_i\) 表示以 \(A_i\) 为开头的子序列不在 \(A_{i\dots |A|}\) 出现的最短长度。

然后从后面挑一个转移即可。

但是我们发现这样子是 \(\mathcal{O}(n^2)\) 的。

但是我们可供转移的字符集最多只有 \(26\) 个。

于是优化状态:设 \(f_i\) 表示以字符 \(i\) 为开头的子序列不在当前 \(A\) 的后缀当中的最短长度。

\[f_i=1+\min_x f_{p_x} \]

然后就做完了。

代码

时间复杂度 \(\mathcal{O}(n)\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 200005
#define M 30
using namespace std;
char s[N + M];
int n;
int p[M],f[M],g[N + M];
signed main(){
	scanf("%s",s + 1);
	n = strlen(s + 1);
	for (int i = 0;i < 26;i ++) s[++n] = i + 'a',p[i] = n;
	for (int i = n - 26;i >= 0;i --) {
		g[i] = p[0];
		for (int j = 1;j < 26;j ++)
			if (f[j] < f[s[g[i]] - 'a']) g[i] = p[j];
		if (i) f[s[i] - 'a'] = f[s[g[i]] - 'a'] + 1,p[s[i] - 'a'] = i;
	}
	for (int x = g[0];x;x = g[x]) putchar(s[x]);
	return 0;
}

入门黑科技:子序列自动机

利用空间换时间的做法。

推荐阅读:https://www.cnblogs.com/zhln/p/18432582

\(nxt_{i,j}\) 表示从 \(i\) 开始字符 \(j\) 最开始的位置。

然后按照序列的顺序跑bfs就可以了。

代码

时间复杂度 \(\mathcal{O}(n)\)

以下代码来自zjc5

#include <bits/stdc++.h>
using namespace std;
const int N = 200010;
char ch[N], ans[N];
int n, nxt[N][26], pre[N], cnt;
bool vs[N];
int main() {
	cin >> (ch + 1);
	n = strlen(ch + 1);
	for (int i = n; i; i--) {
		for (int j = 0; j < 26; j++)
			nxt[i][j] = nxt[i + 1][j];
		nxt[i][ch[i] - 'a'] = i;
	}//预处理
	queue<int>q;
	vs[0] = 1;
	q.push(0);
	while (!q.empty()) {
		int x = q.front();
		q.pop();
		for (int i = 0; i < 26; i++) {
			int t = nxt[x + 1][i];
			if (!t) {//第一个未被找到的字符串
				ans[++cnt] = 'a' + i;
				while (x) {
					ans[++cnt] = ch[x];
					x = pre[x];
				}
				for (int i = cnt; i; i--)
					cout << ans[i];
				return 0;
			} else if (vs[t])
				continue;
			pre[t] = x, vs[t] = 1;
			q.push(t);
		}
	}
	return 0;
}
posted @ 2025-10-12 11:08  high_skyy  阅读(5)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end