Fox And Names CodeForces - 510C

题意:新定义题,重新定义了字典序的概念( 即 a不一定小于b),已的n个单词是按 新字典序 从小到大排序的,求重新定义字典序的字母顺序。如果先后有矛盾输出  “Impossible”。

思路:先把前缀一致的过滤,剩下将路径存入数组,并记录每个字母的入度;

最后拓扑排序:若是不按照26原始字母顺序来,可以直接先将入度为0的全部放入队列并记录答案p[t++],再对队列里的字母开始出队且删除与之有关的边,若删除后,那个字母的入度为0,则可进入队列,答案p[t++],若入度不为0,则不操作。最后若答案长度小于26,说明有环,否则成功输出p数组。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<queue>
 4 #include<stack>
 5 #include <bitset>
 6 #include<set>
 7 #include<map>
 8 #include<unordered_map>
 9 #include<vector>
10 #include<cmath>
11 #include<string>
12 #include<string.h>
13 using namespace std;
14 typedef long long ll;
15 typedef pair<ll, int> pa;
16 typedef unsigned long long ull;
17 char s[105][105];
18 int f[50], mapp[50][50], len[105];
19 char p[1001];//存储拓扑排序结果;
20 void tp() {
21     int i, j, t = 0;
22     queue<int>q;
23     for (i = 0; i < 26; i++){//从小到大找出全部入度为0的字母
24         if (!f[i]){
25             q.push(i);
26             p[t++] = i + 'a';
27         }
28     }
29     while (!q.empty()){//进行拓扑排序
30         int k = q.front();
31         q.pop();
32         for (i = 0; i < 26; i++){
33             if (mapp[k][i] == 1){
34                 f[i]--;
35                 if (f[i] == 0){//直到入度为0才可以进去队列
36                     q.push(i);
37                     p[t++] = i + 'a';
38                 }
39             }
40         }
41     }
42     if (t < 26){
43         printf("Impossible\n");
44     }
45     else{
46         p[t] = '\0';
47         printf("%s\n", p);
48     }
49     return;
50 }
51 
52 int main() {
53     int n;
54     cin >> n;
55     for (int i = 0; i < n; i++) {
56         cin >> s[i];
57         len[i] = strlen(s[i]);
58     }
59     int flag = 0;
60     for (int i = 0; i < n - 1; i++) {
61         int len1 = len[i];
62         int len2 = len[i + 1];
63         int lenm = min(len1, len2);
64         int k, a = 0;
65         //最多比较到较短的单词结束
66         for (k = 0; k < lenm; k++) {
67             if (s[i][k] != s[i+1][k]) {
68                 a = 1;
69                 if (!mapp[s[i][k] - 'a'][s[i+1][k] - 'a']) {//没有出现过这两个字母的连接
70                     mapp[s[i][k] - 'a'][s[i + 1][k] - 'a'] = 1;
71                     f[s[i + 1][k] - 'a']++; //入度+1,相当于在它前面还有一个字母
72                 }
73                 break;
74             }
75         }
76         if (!a) {//若是两个单词中某个是前缀必须是len1
77             if (k != len1) {
78                 flag = 1;
79                 break;
80             }
81         }
82         if (flag) {
83             break;
84         }
85     }
86     if (flag) {
87         printf("Impossible\n");
88     }
89     else {
90         tp();
91     }
92     return 0;
93 }

若是按照26字母原始顺序,则遍历每次取出第一个入度为0的字母,依次删除与之有关的边,入度为负记录答案num[cns++],再取出第一个入读为0的边,直到在26次循环中某一次没有找到入度为0的边,说明有环。代码参考:https://blog.csdn.net/qq_41890797/article/details/81742221

 1 void tp()  //拓扑排序的核心代码
 2 {
 3     int p,flag,cns=0;
 4     for(int i=0; i<26; i++)
 5     {
 6         int flag=0;
 7         for(int j=0; j<26; j++)
 8         {
 9             if(!f[j])  //此时这个字母是最小的
10             {
11                 p=j;
12                 flag=1;
13                 break;
14             }
15         }
16         if(!flag) //没有可确定的字母,矛盾了
17         {
18             printf("Impossible\n");
19             return;
20         }
21         f[p]--;
22         num[cns++]=p+'a';
23         for(int j=0; j<26; j++)
24             if(map[p][j])
25                 f[j]--;    //与p为前端的入度-1
26     }
27     num[cns]=0;//加 ‘/0’
28     puts(num);
29 }

 

posted @ 2020-08-06 00:25  吉吉的奥利奥  阅读(8)  评论(0编辑  收藏