SOJ 1008

1008. Translations

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB

Description

Bob Roberts is in charge of performing translations of documents between various languages. To aid him in this endeavor his bosses have provided him with translation files. These files come in twos -- one containing sample phrases in one of the languages and the other containing their translations into the other language. However, some over-zealous underling, attempting to curry favor with the higher-ups with his initiative, decided to alphabetically sort the contents of all of the files, losing the connections between the phrases and their translations. Fortunately, the lists are comprehensive enough that the original translations can be reconstructed from these sorted lists. Bob has found this is most usually the case when the phrases all consist of two words. For example, given the following two lists: Language 1 Phrases Language 2 Phrases arlo zym bus seat flub pleve bus stop pleve dourm hot seat pleve zym school bus Bob is able to determine that arlo means hot, zym means seat, flub means school, pleve means bus, and dourm means stop. After doing several of these reconstructions by hand, Bob has decided to automate the process. And if Bob can do it, then so can you.

Input

Input will consist of multiple input sets. Each input set starts with a positive integer n, n 250, indicating the number of two-word phrases in each language. This is followed by 2n lines, each containing one two-word phrase: the first n lines are an alphabetical list of phrases in the first language, and the remaining n lines are an alphabetical list of their translations into the second language. Only upper and lower case alphabetic characters are used in the words. No input set will involve more than 25 distinct words. No word appears as the first word in more than 10 phrases for any given language; likewise, no word appears as the last word in more than 10 phrases. A line containing a single 0 follows the last problem instance, indicating end of input.

Output

For each input set, output lines of the form word1/word2 where word1 is a word in the first language and word2 is the translation of word1 into the second language, and a slash separates the two. The output lines should be sorted according to the first language words, and every first language word should occur exactly once. There should be no white space in the output, apart from a single blank line separating the outputs from different input sets. Imitate the format of the sample output, below. There is guaranteed to be a unique correct translation corresponding to each input instance.

Sample Input

4
arlo zym
flub pleve
pleve dourm
pleve zym
bus seat
bus stop
hot seat
school bus
2
iv otas
otas re
ec t
eg ec
0

Sample Output

arlo/hot
dourm/stop
flub/school
pleve/bus
zym/seat

iv/eg
otas/ec

re/t

比较明显是要对每种语言建立有向图(这里建图时采用第一个单词节点指向第二个),然后判断图是否同构,在找出两种有向图中的对应节点。主要思路还是用邻接矩阵来描述有向图,每个节点存储在一维数组里,然后用深搜查找匹配。没有刻意剪枝也没有超时,但是也找到了一种剪枝的方法:对于第一张图中的某个节点,若第二张图中与其出入度相同的节点只有一个,则其必定相对应,不用再进行下一步验证,可以直接DFS(pos+1),这样剪枝应该能进一步提高性能。具体过程写在了代码注释里。

#include <iostream>
#include <map>
#include<string>
#include <iterator>
using namespace std;

struct node{//每种语言的每个单词对应一个node数据类型 
	string s;//存储单词字符串 
	int match;//这一单词在另一个语言中对应单词在数组中的下标号 
	int outDegree;//这一单词的出度 
	int inDegree;//这一单词的入度 
	node(){
	}
	node(string str,int Match,int out,int in)
	{
		s=str;
		match=Match;
		outDegree=out;
		inDegree=in;
	}
};

node N1[25],N2[25];//node类型的数组,N1用来存储第一种语言的单词节点,N2用来存储第二种语言的单词节点 
map<string,int> M1,M2;//map容器,M1存储第一种语言单词字符串和和其在node数组中下标的对应关系,M2亦然 
int G1[25][25],G2[25][25];//int型二维数组用来存储每一对单词的先后顺序,即有向图中节点的方向。初始全置为0,若0号节点指向1号节点,则G[0][1]为1 
int n,n1,n2;//n为题目描述中的n,n1用来记录第一种语言中的单词数,n2亦然 
bool flag;//用来标记是否成功匹配 

void resetG()//用来重置两个二维数组的函数 
{
	for(int i=0;i<25;++i)
	{
		for(int j=0;j<25;++j)
		{
			G1[i][j]=G2[i][j]=0;
		}
	}
}

void makeG()//建立两个有向图 
{
	M1.clear();
	M2.clear();//清空两个map容器 
	resetG();//重置二维数组 
	n1=n2=0;//初始单词数记为0 
	string s1,s2;
	for(int i=0;i<n;++i)//建立第一种语言对应的有向图 
	{
		cin>>s1>>s2;
	    if(M1.find(s1)==M1.end())//若在容器中未查找到s1,则在容器中添加 
	    {
	    	M1[s1]=n1;
	    	N1[n1]=node(s1,-1,1,0);//同时将s1对应的节点添加到node数组中 
	    	n1++;
	    }
	    else
	    {
	    	N1[M1[s1]].outDegree++;//若s1已被记录,由于s1对应的节点在前,所以出度加1 
		}
		if(M1.find(s2)==M1.end())//对s2同理 
	    {
	    	M1[s2]=n1;
	    	N1[n1]=node(s2,-1,0,1);//match成员被初始化为-1表示尚未找到匹配 
	    	n1++;
	    }
	    else
	    {
	    	N1[M1[s2]].inDegree++;
		}
		G1[M1[s1]][M1[s2]]=1;//记录这一对单词的方向 
	}
	for(int i=0;i<n;++i)//同理建立第二种语言对应的有向图 
	{
		cin>>s1>>s2;
	    if(M2.find(s1)==M2.end())
	    {
	    	M2[s1]=n2;
	    	N2[n2]=node(s1,-1,1,0);
	    	n2++;
	    }
	    else
	    {
	    	N2[M2[s1]].outDegree++;
		}
		if(M2.find(s2)==M2.end())
	    {
	    	M2[s2]=n2;
	    	N2[n2]=node(s2,-1,0,1);
	    	n2++;
	    }
	    else
	    {
	    	N2[M2[s2]].inDegree++;
		}
		G2[M2[s1]][M2[s2]]=1;
	}
}

void DFS(int pos)//深搜进行匹配查找 
{
	if(pos==n1)
	{
		flag=1;
		return;
	}
	for(int i=0;i<n2;++i)//利用循环在第二种语言中寻找匹配 
	{
		if(N2[i].match==-1&&N1[pos].outDegree==N2[i].outDegree&&N1[pos].inDegree==N2[i].inDegree)//两种语言中两个节点的对应出入度都相等 
		{
			int j;
			for(j=0;j<pos;++j)//再验证在第一种语言中已匹配的指向pos的节点在第二种语言中的匹配节点是否也指向i;同时验证pos指向的节点在第二种语言的对应节点是否也被i所指 
			{
				if(G1[j][pos]&&!G2[N1[j].match][i])//不满足第一种情况则跳出循环 
				{
					break;
				}
				if(G1[pos][j]&&!G2[i][N1[j].match])//不满足第二种情况则跳出循环 
				{
					break;
				}
			}
			if(j==pos)//若相等则代表未曾跳出过循环,则可认为pos的对应节点为i 
			{
				N1[pos].match=i;
				N2[i].match=pos;
				DFS(pos+1);
				if(flag)
				return;
				N1[pos].match=N2[i].match=-1;//回溯
			}
		}
	}
}

int main()
{
	bool first=true;//注意输出格式要求每次结果后输出一个空白行,但在最后一次输出结果时不需打印空白行 
	while(cin>>n&&n)
	{
		flag=0;
		makeG();
		DFS(0);
		if(!first) cout<<endl;
		first=false;
		for(map<string,int>::iterator iter=M1.begin();iter!=M1.end();++iter)//题目要求输出时按单词的首字母顺序输出,由于在map容器中添加元素时会默认按照元素的key值的字典序排序,所以用迭代器遍历容器来输出而不是遍历node数组来输出(否则要多出对数组排序的步骤) 
		{
			cout<<iter->first<<"/"<<N2[N1[iter->second].match].s<<endl;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值