[日常训练] 单词

来源:互联网 发布:手机淘宝查看卖家电话 编辑:程序博客网 时间: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;} 
原创粉丝点击