P6286
P6286 Cezar 题解
题意
求一个密钥,使加密后的单词按给定顺序排列。
转化
不妨设密钥为 \(k_i\),其中 \(i \in [1,26]\)。
\(k_i\),表示字母 \(i\) 要替换为字母 \(k_i\)。
转化一下,密钥就等于,钦定字母 \(i\) 是字典中的第 \(k_i\) 大(重定义字典序)。然后就不用考虑替换了。
问题等价于,问一个新的字母大小顺序,使得 \(n\) 个单词按给定顺序排列。
实现
这个很好做,图论建模。设给定顺序为 \(Order_i\),只要保证 \(\forall i \in [1,n)\),\(s_i < s_{i + 1}\) 即可。
\(s_i\) 是单词,\(<\) 是新字典序下的字符串比较。
那么我们就有了 \(n - 1\) 组形如 \(s_i < s_j\) 的字符串关系。
对于每组关系,找到最小的 \(k\) 使得 \(s_{i_k} \neq s_{j_k}\)。若存在,那么字母 \(s_{i_k}\) 的字典序小于 \(s_{j_k}\),在图中加有向边。
若不存在,分类讨论:
-
\(s_i\) 是 \(s_j\) 前缀,根据字典序的定义,大小关系成立。
-
\(s_j\) 是 \(s_i\) 前缀,根据字典序定义,恒不成立,输出无解。
然后要满足这些字母大小关系,拓扑即可。
最后,拓扑完之后入度数组没有清零的话,就存在环,输出无解。
设字符串长度为 \(L\),时间复杂度 \(O(n \times L + C)\),其中 \(C = 26\)。
//Problem: P6286
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define i32 INT_MAX
#define i64 LONG_LONG_MAX
#define pii std::pair<int, int>
#define pll std::pair<long long, long long>
#define pb push_back
#define fore(i,u,v) for(int i=head[u],v;i;i=e[i].nxt)
typedef long long ll;
const int N = 105;
ll read(){ll x=0,f=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}return x*f;}
void print(ll x){if(x<0)putchar('-'),x=-x;if(x>9)print(x/10);putchar(x%10+48);}
char gc(){char c=getchar();while(c==' '||c=='\n')c=getchar();return c;}
void pc(char Char){std::putchar(Char);}
int n, cnte, len;
int Order[N], Len[N], head[27], in[27], List[27], ans[27];
char a[N][N];
struct edge {
int v, nxt;
} e[N];
std::queue <int> q;
void adde(int u, int v) {
e[++cnte] = (edge){v, head[u]};
head[u] = cnte;
in[v]++;
}
void TopoSort() {
for(int i = 1; i <= 26; i++) if(!in[i]) List[++len] = i, q.push(i);
while(!q.empty()) {
int u = q.front();
q.pop();
fore(i, u, v) {
v = e[i].v;
in[v]--;
if(in[v] == 0) {
List[++len] = v;
q.push(v);
}
}
}
}
int main() {
// freopen("P6286_3.in", "r", stdin);
// freopen("out.out", "w", stdout);
std::cin >> n;
for(int i = 1; i <= n; i++) scanf(" %s", a[i] + 1), Len[i] = strlen(a[i] + 1);
for(int i = 1; i <= n; i++) Order[i] = read();
for(int i = 1; i < n; i++) {
int Flag = 0;
for(int k = 1; k <= Len[Order[i]] && k <= Len[Order[i + 1]]; k++) {
if(a[Order[i]][k] != a[Order[i + 1]][k]) {
adde(a[Order[i]][k] - 'a' + 1, a[Order[i + 1]][k] - 'a' + 1);
// std::cout << a[Order[i]][k] << ' ' << a[Order[i + 1]][k] << '\n';
Flag = 1;
break;
}
}
if(!Flag) {
if(Len[Order[i]] > Len[Order[i + 1]]) {
puts("NE");
return 0;
}
}
}
TopoSort();
for(int i = 1; i <= 26; i++) {
if(in[i] != 0) {
puts("NE");
return 0;
}
// std::cout << in[i] << '\n';
}
puts("DA");
for(int i = 1; i <= 26; i++) ans[List[i]] = i;
for(int i = 1; i <= 26; i++) {
pc(ans[i] + 'a' - 1);
}
return 0;
}