遗迹之门


Description

NightElf 是一个古老的种族,在他们的遗迹上到处都有古NightElf 的奇妙文字,这些文字都是一些十分美妙而复杂的符号。然而,现在的NightElf 人早已不认识古文字了,因为古文字过于复杂,在几十个世纪前,他们就改用简单的英文字母来代替古代的文字符号,每个古文字符号对应一个英文字母。当然,正是因为对应关系渐渐被人遗忘、,所以古代文字已经无法阅读。除了这一点之外,古语同现代语没有任何区别。有一天,探险家Arthas 在一个古老的NightElf 遗迹前面被一扇门给拦住了。大门上用NightElf 文字刻着一句话。当地人告诉他,只有知道这句话的意思,解出一个谜题,才能进入这个遗迹。 

于是 Arthas 下决心要找出古文字和现代的英文字母之间的联系,理解这句话的含义。然而这并不是一件轻松的工作。所辛,他的一个朋友是语言学家,告诉他一些很重要的事实:NightElf 文字共有n(1<=n<=27)个文字符号。其中n-1 个文字是有意义的,这n-1 个文字在现代语中用小写字母a,b,…中的前 n-1 个表示。还有一个文字是分隔符。 

在句子中每个单词之间有且仅有一个分隔符,将单词隔开。另外NightElf 语言中的合法单词有着非常丰富的意义。因此只有很少的单词。当然,遗迹之门上的那一句话的每个单词都应该是NightElf 语言中的合法单词。于是Arthas想通过了解NightElf 语言中的所有单词,来试着找出句子的含义。

Input

第一行一个整数 t,表示有t 组测试数据。 
每组数据第一行是两个整数n(1<=n<=27)和m(1<=m<=150),分别表示NightElf 语言的字符数和句子的字符数。 
接着一行有 m 个整数,每个整数都在1 到n 之间,且1 到n 中的每一个数都会出现。每个数代表一个古NightElf 语言的文字符号,相同的数字表示相同的文字符号。 
再接下来一个整数q(1<=q<=200),表示NightElf 语言的单词数。 
接下来有 q 行,每行一个有现代语写出的(即用小写字母表示)单词。每个单词的长度不超过10。

Output

输出一行,是 Arthas 对输入古文的分析结果: 
1)如果句子只有惟一的可能,将它翻译成现代语输出; 
2)如果没有任何可能的单词组合组成句子,输出No solution; 
3)如果句子有多种可能的组合,输出Cannot determine。

Sample Input

 

5 18 
3 2 1 3 5 4 2 1 1 5 2 4 3 4 5 3 4 1 
14 
adca 
bacb 
cacc 
adba 
cabb 
bdcc 
dacc 
baaa 
dbab 
acbc 
daba 
cba 
abc 
dcb

 

Sample Output

 

adca bdcc dbab abc

 

Hint

40%的数据满足: t=1,n<=6,m<=30,q<=40 
100%的数据满足: t<=3,n<=27,m<=150,q<=200


【分析】

 

    寻找集合{1,2,}与集合{a,b,}的一一对应关系,实质是一个排列生成问题。

    Step1确定分隔符(空格),需要穷举每个数字能否作为空格,原则是:一旦确定i是空格,则特殊的语言中分出来形成单词的长度要在单词表中出现才行。这可以排除显然无解的情况:例如样例中,以1作为空格,则第一个单词为3 2,而单词表中没有长度为2的单词。

    Step2搜索对应关系: 

    搜索策略1:用排列搜索出一种对应关系,再判断是否合法,时间复杂度O(n!)

    搜索策略2:按照给出的特殊语言a[1]..a[m]的顺序进行搜索对应关系,这样的搜索顺序为我们剪枝提供了方便:

    剪枝搜索单词的时候,用一个数组建立对应关系,若发现当前字母与之前的对应关系有冲突,剪枝。

    这个剪枝相当强,只需这个剪枝便可以100ms左右过了。


【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
using namespace std;
int T,N,M,Nt,ANS,v[300],Q;
string w[300],s;
char match[30],path[30];  //path数组记录答案
bool f[30];
int ans;
void _DFS(int k)
{
    if(k>M)
    {
        memcpy(path,match,sizeof(match));
        ans++;
        ANS=N;       //记录取得答案的分隔符
        return;
    }
    bool ft[30];
    char mt[30];
    memcpy(ft,f,sizeof(f));
    memcpy(mt,match,sizeof(match));
    for(int i=1;i<=Q;i++)
    {
         memcpy(f,ft,sizeof(ft));
         memcpy(match,mt,sizeof(mt));
         int j,p=1;
         for(j=k;v[j]!=N&&j<=M;j++);
         j--;
         if((j+1-k)!=w[i].length())
             continue;
         for(int o=k;o<=j;o++)
         {
             int oi=o+1-k;
             //剪枝
             if((('a'<=match[v[o]]&&match[v[o]]<='z')||(f[w[i][oi-1]-'a']==true))&&match[v[o]]!=w[i][oi-1])
             {
                 p=0;  
                 break;
             }
             match[v[o]]=w[i][oi-1];
             f[w[i][oi-1]-'a']=true;
         }
         if(p==0)
             continue;
          _DFS(j+2);
          if(ans>=2)
              return;
    }  
}
void _init()
{
    ans=0;
    memset(path,0,sizeof(path));  //注意初始化数组
    memset(v,0,sizeof(v));
    scanf("%d%d",&N,&M);
    for(int i=1;i<=M;i++)
        scanf("%d",&v[i]);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)
        cin>>w[i];
    Nt=N;
}
void _solve()
{
    for(N=1;N<=Nt;N++)     //枚举哪个数字作为分隔符
    {
       
       for(int i=1;i<=Q;i++)
       {
            int j,p=1;
             memset(match,0,sizeof(match));
             memset(f,0,sizeof(f));
             for(j=1;v[j]!=N;j++);
             j--;
             if(j!=w[i].length())
                 continue;
             for(int o=1;o<=j;o++)
             {
                 if((('a'<=match[v[o]]&&match[v[o]]<='z')||(f[w[i][o-1]-'a']==true))&&match[v[o]]!=w[i][o-1])
                 {
                     p=0;
                     break;
                 }
                 match[v[o]]=w[i][o-1];
                 f[w[i][o-1]-'a']=true;
             }
             if(p==0) continue;
              _DFS(j+2);
              if(ans>=2)
              {
                  printf("Cannot determine\n");
                  return;
              }  
       }
    }
    if(ans)
    {
        for(int i=1;i<=M;i++)
        {
            if(v[i]==ANS)
                printf(" ");
            else if('a'<=path[v[i]]&&path[v[i]]<='z')
                printf("%c",path[v[i]]);
        }
        printf("\n");
    }
    else
       printf("No solution\n");
}
int main()
{
   scanf("%d",&T);
   for(int i=1;i<=T;i++)
   {
       _init();
       _solve();
   }
   return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值