P6286 [COCI2016-2017#1] Cezar (字典树+拓扑排序)
字典树+拓扑排序
没看题绕了半天。根据字符串的比较,容易想到用字典树。直接枚举两个字符串,找到最大公共前缀,根据排名连边。跑一遍拓扑排序后就可以得到”哪些字母字典序需要更小“这样一个从左到右的顺序。然后越靠前的字母,它位置上的替换字母就越小。
PS:注意特判 aa
比 aaa
字典序更大的情况,一定无解。
复杂度 \(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;
}