UVa Problem 10150 Doublets (Doublets 序列)

// Doublets (Doublets 序列)
// PC/UVa IDs: 110307/10150, Popularity: C, Success rate: average Level: 3
// Verdict: Accepted
// Submission Date: 2011-05-20
// UVa Run Time: 0.200s
//
// 版权所有(C)2011,邱秋。metaphysis # yeah dot net
//
// 此题目可以转换为求无权图的最短路问题,可以用 BFS 来实现。由于最长的单词不超过 16 个字母,故可
// 将单词按长度分为 16 种类别,两个不同长度的单词不会构成 Doublets,这样每种类别平均单词个数大
// 概为 25143/16,同时只在需要时才构建相应长度单词的图表示。使用map或二分查找(先排序)来取得单
// 词在数组中的序号可能会获得更好的 RT。本程序在 Programming Challenges 上结果为 WA。在 UVa
// 上为 AC。
	
#include <iostream>
#include <sstream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <queue>
#include <cstring>
	
using namespace std;
	
#define MAXSIZE 25143		// 字典最大数量为25143。
	
// 判断两个单词是否组成一对 Doublets。
bool doublets(string &a, string &b)
{
	// 长度不等,不会是 Doublets。
	if (a.length() != b.length())
		return false;
	
	// 寻找两个单词不同的个数。
	int diff = 0;
	for (int i = 0; i < a.length(); i++)
		if (a[i] != b[i])
		{
			diff++;
			if (diff > 1)
				return false;
		}
	
	return diff == 1;
}
	
void no_solution()
{
	cout << "No solution." << endl;
}
	
void find_path(vector < string > &points, int parent[MAXSIZE], int x, int y)
{
	// 根据 parent 数组回溯得到最短路。从终点 y 往回找,如果父节点为 -1 则表示未找到,如
	// 果为起点x则输出此条路径。
	vector < int > path;
	while (parent[y] != x && parent[y] != -1)
	{
		path.push_back(y);
		y = parent[y];
	}
	
	if (parent[y] == -1)
		no_solution();
	else
	{
		path.push_back(y);
		path.push_back(x);
		for (int m = path.size() - 1; m >= 0; m--)
			cout << points[path[m]] << endl;
	}
}
	
// 使用 BFS(宽度优先搜索)查找最短路并输出。
void bfs(vector < vector < int > > &edges, int parent[MAXSIZE], int x, int y)
{
	// 查找序号为 x 到序号为 y 的单词间的最短路径。
	bool discovered[MAXSIZE];
	queue < int > q;
	int v;
	
	memset(discovered, 0, sizeof(discovered));
	memset(parent, -1, sizeof(parent));
	
	q.push(x);
	discovered[x] = true;
	
	// BFS 遍历图直到遍历完毕或找到序号为 y 的单词。
	bool finish = false;
	while (q.size())
	{
		v = q.front();
		q.pop();
		for (int i = 0; i < edges[v].size(); i++)
		{
			if (!discovered[edges[v][i]])
			{
				q.push(edges[v][i]);
				discovered[edges[v][i]] = true;
				parent[edges[v][i]] = v;
				if (edges[v][i] == y)
				{
					finish = true;
					break;
				}
			}
		}

		if (finish)
			return;
	}
}
	
int main(int ac, char *av[])
{
	vector < vector < int > > edges[16];
	vector < string > points[16];
	bool inited[16];
	string line;
	int m, n;
	
	memset(inited, false, sizeof(inited));
	
	// 读入单词字典数据。
	while (getline(cin, line), line.length() > 0)
	{
		m = line.length() - 1;
		points[m].push_back(line);
	}
	
	int cases = 0;
	while (cases++, getline(cin, line))
	{
		istringstream iss(line);
		string a, b;
		iss >> a >> b;
	
		// 输出空行。
		if (cases > 1)
			cout << endl;
	
		// 若单词长度不等,无解。
		if (a.size() != b.size())
		{
			no_solution();
			continue;
		}
	
		// 找到单词 a 和 b 在图中的序数。
		int x = -1, y = -1;
		int m = a.length() - 1;
		
		// 在图中找到单词 a 和 b 的序号。
		for (int i = 0; i < points[m].size(); i++)
		{
		    if (x == -1 && a == points[m][i])
		        x = i;

		    if (y == -1 && b == points[m][i])
		        y = i;

		    if (x != -1 && y != -1)
		        break;
		}
			
		// 未找到。
		if (x == -1 || y == -1)
			no_solution();
		// 找到,但是单词长度均为 1。
		else if (a.length() == 1 && b.length() == 1)
		{
			// 长度均为 1,但两者不同,直接输出。
			if (a != b)
				cout << a << endl << b << endl;
			else
			{
				// 因为所有单词互不相同,故只要找到一个单词,它与x和y均不同即可。
				// 当然必须单词数大于 2 个。
				if (points[0].size() > 2)
				{
					// 因为所有单词互不相同,故只要找到一个序号,它与
					//  x 和 y 均不同即可。当然必须单词数大于 1 个。
					cout << a << endl;
					for (int i = 0; i < points[0].size(); i++)
						if (i != x && i != y)
						{
							cout << points[0][i] << endl;
							break;
						}
					cout << b << endl;
				}
				else
					no_solution();
			}
		}
		else
		{
			// 单词长度为(m + 1)的图尚未构建,则构建。
			if (!inited[m])
			{
				edges[m].resize(points[m].size());
				for (int i = 0; i < points[m].size() - 1; i++)
					for (int j = (i + 1); j < points[m].size(); j++)
						if (doublets(points[m][i], points[m][j]))
						{
							edges[m][i].push_back(j);
							edges[m][j].push_back(i);
						}

				inited[m] = true;
			}
	
			// 两者长度不为 1,但两个单词相同,则只需要查看该单词是否有 Doublets,
			// 有则将单词作为中间输出。
			if (x == y)
			{
				if (edges[m][x].size())
				{
					cout << a << endl;
					cout << points[m][edges[m][x][0]] << endl;
					cout << b << endl;
				}
				else
					no_solution();
			}
			else
			{
				int parent[MAXSIZE];
				
				// 使用 BFS(宽度优先搜索)查找最短路并输出。
				bfs(edges[m], parent, x, y);

				find_path(points[m], parent, x, y);
			}
		}
	}
	
	return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值