[日常训练] 单词
来源:互联网 发布:手机淘宝查看卖家电话 编辑:程序博客网 时间:2024/06/07 08:00
【问题描述】
在一种未知语言中,很多单词被发现了,但是他们的字母的字典序我们是不知道的。我们知道的是,这些单词是按照字典序从小到大排列的。
或者找出这种语言唯一的字母的字典序,或者得出这种方案是不存在的,或者得出有很多种这样的方案。
【输入格式】
第一行包括一个正整数n(1 <= n <= 100),表明单词的数量。
接下来n行,每行一个单词,每个单词最多包含10个小写的英文字母。保证单词以未知语言的字典序给出。
【输出格式】
有且仅有一行,输出包含所有字母的字典序。如果没有这种字典序,则输出“!”,如果有多种方案则输出“?”。
【输入样例1】
5
ula
uka
klua
kula
al
【输出样例1】
luka
【输入样例2】
4
jaja
baba
baja
beba
【输出样例2】
!
【输入样例3】
3
marko
darko
zarko
【输出样例3】
?
【数据范围与约定】
对于30%的数据:n <= 20。
对于100%的数据:n <= 100。
【解法1】拓扑排序
- 确定一个唯一的字典序,我们可以想到拓扑排序
- 若两个字符串
si,sj(i<j) 已经按照字典序从小到大排序,那么我们找到它们从起始位置开始的第一个不同的字母,则这两个字母si[k],sj[k] 也满足从小到大的字典序,建边si[k]→sj[k] 。 - 然后对这样建成的图进行拓扑排序
- 若图中存在环,则字典序中存在自相矛盾的情况,需要输出”!”,这时拓扑排序显然是无法遍历全部点的,我们记录总的点数以及遍历到的点数加以判断
- 若通过某一个点更新其它点的入度时,发现此时存在多个点入度为0,则这些点的顺序是不能确定的,需要输出”?”,但要注意若已经发现上述”!”的情况,则应优先输出”!”
- 若上述两种情况都未出现,直接输出拓扑序。
【代码1】
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int N = 105, M = N * N;char s[N][15]; bool G[27][27], vis[27], sf; int E, n, top, stp;int l[N], stk[N], pos[N], rin[N];inline int Min(const int &x, const int &y){return x < y ? x : y;}struct Edge{ int to; Edge *nxt;}a[M], *T = a, *lst[N];inline void addEdge(const int &x, const int &y){ (++T)->nxt = lst[x]; lst[x] = T; T->to = y; rin[y]++;}int main(){ freopen("words.in", "r", stdin); freopen("words.out", "w", stdout); scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%s", s[i]), l[i] = strlen(s[i]); for (int i = 1; i <= n; ++i) for (int j = i + 1; j <= n; ++j) { int ml = Min(l[i], l[j]); for (int k = 0; k < ml; ++k) { int tx = s[i][k] - 'a', ty = s[j][k] - 'a'; if (tx == ty) continue; if (!G[tx][ty]) addEdge(tx, ty); G[tx][ty] = true; break; } for (int k = 0; k < l[i]; ++k) vis[s[i][k] - 'a'] = true; for (int k = 0; k < l[j]; ++k) vis[s[j][k] - 'a'] = true; } /* for (int i = 0; i < 26; ++i) for (Edge *e = lst[i]; e; e = e->nxt) printf("%c -> %c\n", i + 'a', e->to + 'a'); */ int x, y, cnt = 0, num = 0; for (int i = 0; i < 26; ++i) if (vis[i]) { stp++; if (!rin[i]) { stk[++top] = pos[++E] = i; cnt++; if (cnt > 1) sf = true; } } while(top) { x = stk[top--]; cnt = 0; num++; for (Edge *e = lst[x]; e; e = e->nxt) if (!(--rin[y = e->to])) { cnt++; stk[++top] = pos[++E] = y; if (cnt > 1) sf = true; } } if (num != stp) puts("!"); else if (sf) puts("?"); else for (int i = 1; i <= E; ++i) putchar(pos[i] + 'a'); fclose(stdin); fclose(stdout); return 0;}
【解法2】Floyd
- 记
bool 数组f[x][y] 表示字母x 的字典序是否小于字母y ,初始时的建图同上 - 若存在
f[x][y]=f[y][x]=1 ,则字典序中存在自相矛盾的情况,输出”!” - 若存在
f[x][y]=f[y][x]=0 ,则字母x,y 的字典序大小关系无法确定,可能有多种方案,输出”?” - 否则我们根据
f[x][y] ,统计出字典序中比x 大的字母个数,就可以确定x 在字典序中的排名,最后按顺序输出即可
【代码2】
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int N = 105;char s[N][15], Ans[N]; int l[N], num, n, tp;bool f[27][27], vis[27], sf; inline int Min(const int &x, const int &y) {return x < y ? x : y;}int main(){ freopen("words.in", "r", stdin); freopen("words.out", "w", stdout); scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%s", s[i]), l[i] = strlen(s[i]); for (int i = 1; i <= n; ++i) for (int j = i + 1; j <= n; ++j) { int ml = Min(l[i], l[j]); for (int k = 0; k < ml; ++k) if (s[i][k] != s[j][k]) { f[s[i][k] - 'a'][s[j][k] - 'a'] = true; break; } for (int k = 0; k < l[i]; ++k) vis[s[i][k] - 'a'] = true; for (int k = 0; k < l[j]; ++k) vis[s[j][k] - 'a'] = true; } for (int k = 0; k < 26; ++k) for (int i = 0; i < 26; ++i) for (int j = 0; j < 26; ++j) f[i][j] |= f[i][k] & f[k][j]; for (int i = 0; i < 26; ++i) if (vis[i]) { int num = 0; tp++; for (int j = 0; j < 26; ++j) if (vis[j] && i != j) { if (!f[i][j] && !f[j][i]) sf = true; if (f[i][j]) { num++; if (f[j][i]) { puts("!"); fclose(stdin); fclose(stdout); return 0; } } } Ans[num] = i + 'a'; } if (sf) puts("?"); else for (int i = tp - 1; i >= 0; --i) putchar(Ans[i]); fclose(stdin); fclose(stdout); return 0;}
阅读全文
1 0
- [日常训练] 单词
- HEU日常训练10.02
- 日常训练小结
- 日常训练20161012 道路网
- 日常训练20161012 醉酒
- 日常训练20161014 跟踪
- 日常训练20161018 证据
- 日常训练20161018 subset
- 日常训练 平均数
- 日常训练 水箱
- 日常训练 棋盘游走
- 日常训练 20170531 数字
- 日常训练 20170531 探险
- 日常训练 20170531 矩阵
- 日常训练 20170602 Book
- 日常训练 20170602 Equation
- 日常训练 20170603 棋盘
- 日常训练 20170605 EasyProblem
- web.xml配置详解
- android富文本 加载带图片的html<drawable为null的bug>
- java实现手机扫二维码登陆
- 浅谈list和truple(python3)
- SpringBoot+Mybatis 完整实例 增删查改
- [日常训练] 单词
- poj Stall Reservations(贪心)
- 从零开始开发一款Android App
- vi 常用命令
- android开发之四种基本布局
- Service基础
- 扫码微信开发中的一些事
- window.open详解
- php 支持断点续传的文件下载类