Loading

P6286 [COCI2016-2017#1] Cezar (字典树+拓扑排序)

P6286 [COCI2016-2017#1] Cezar

字典树+拓扑排序

没看题绕了半天。根据字符串的比较,容易想到用字典树。直接枚举两个字符串,找到最大公共前缀,根据排名连边。跑一遍拓扑排序后就可以得到”哪些字母字典序需要更小“这样一个从左到右的顺序。然后越靠前的字母,它位置上的替换字母就越小

PS:注意特判 aaaaa 字典序更大的情况,一定无解。

复杂度 \(O(n^2\sum size_i)\)

我用的另一种方法。考虑对于字典树上每个节点下的每个子节点,其子树下包含若干字符串,排名已知。并且该字符位置决定了整个子树中的字符串在前还是在后,我们只关心子树中的最大值和最小值,构成一个区间。如果两个子树对应的区间有交,那么一定无解,否则就可以确定出顺序。然后也是跑一遍拓扑排序,不说了。

复杂度 \(O(26^2\sum size_i)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;

int n, tot, num;
std::string s[110];
int tr[10010][28], min[10010], max[10010], in[30], ans[30], ret[30];
void insert(std::string s, int k) {
	int u = 0;
	for(int i = 0; i < s.length(); i++) {
		int c = s[i] - 'a';
		if(!tr[u][c]) tr[u][c] = ++tot;
		u = tr[u][c];
	}
	min[u] = std::min(min[u], k);
	max[u] = std::max(max[u], k);
}
bool flg = 0;
std::vector<int> e[30];
void dfs(int u) {
	for(int c = 0; c < 26; c++) {
		if(tr[u][c]) {
			dfs(tr[u][c]);
			min[u] = std::min(min[u], min[tr[u][c]]);
			max[u] = std::max(max[u], max[tr[u][c]]);
		}
	}
	for(int i = 0; i < 26; i++) {
		if(!tr[u][i]) continue;
		for(int j = i + 1; j < 26; j++) {
			if(!tr[u][j]) continue;
			if(max[tr[u][i]] < min[tr[u][j]]) e[i].pb(j), in[j]++;
			else if(max[tr[u][j]] < min[tr[u][i]]) e[j].pb(i), in[i]++;
			else if(max[tr[u][i]] > min[tr[u][j]] && max[tr[u][j]] > min[tr[u][i]]) flg = 1; 
		}
	}
}
bool check(std::string s) {
	int ret = 0;
	int u = 0;
	for(int i = 0; i < s.length(); i++) {
		int c = s[i] - 'a';
		if(!tr[u][c]) tr[u][c] = ++tot;
		u = tr[u][c];
		ret = std::max(ret, max[u]);
	}
	return ret > min[u];
} 
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n;

	for(int i = 1; i <= n; i++) {
		std::cin >> s[i];
	}
	memset(min, 0x3f, sizeof(min));
	for(int i = 1; i <= n; i++) {
		int a;
		std::cin >> a;
		insert(s[a], i);
	}
	for(int i = 1; i <= n; i++) if(check(s[i])) flg = 1;

	dfs(0);

	if(flg) {
		std::cout << "NE\n";
		return 0;
	}

	std::cout << "DA\n";
	std::queue<int> q;
	for(int i = 0; i < 26; i++) if(!in[i]) q.push(i);
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		ret[num++] = u;
		for(auto v : e[u]) {
			in[v]--;
			if(!in[v]) q.push(v);
		}
	}
	for(int i = 0; i < 26; i++) {
		ans[ret[i]] = i;
	}
	for(int i = 0; i < 26; i++) std::cout << char(ans[i] + 'a');
	return 0;
}
posted @ 2024-06-29 14:15  Fire_Raku  阅读(5)  评论(0编辑  收藏  举报