Timus : 1002. Phone Numbers 题解

把电话号码转换成为词典中可以记忆的的单词的组合,找到最短的组合。


我这道题应用到的知识点:

1 Trie数据结构

2 map的应用

3 动态规划法Word Break的知识

4 递归剪枝法


思路:

1 建立Trie字典树,方便查找, 但是字典树不是使用字符来建立的,而是把字符转换成数字,建立一个数字字典树, 然后叶子节点设置一个容器vector<string>装原单词。

2 动态规划建立一个表,记录可以在字典树中找到的字符串的前缀子串

3 如果找到整个串都在字典树中,那么就可以直接返回这个单词。如果无法直接找到,那么就要在表中找到一个前缀子串,然后后面部分在字典树中查找,看是否找到包含这个子串的单词,并且要求找到的单词长度最短。- 这里可以使用剪枝法提高效率。

原题:http://acm.timus.ru/problem.aspx?space=1&num=1002


作者:靖心 - http://blog.csdn.net/kenden23


#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <algorithm>
#include <unordered_map>

using namespace std;

class PhoneNumber1002_2
{
	static const int SIZE = 10;
	struct Node
	{
		vector<string> words;
		Node *children[SIZE];
		explicit Node () : words()
		{
			for (int i = 0; i < SIZE; i++)
			{
				children[i] = NULL;
			}
		}
	};

	struct Trie
	{
		Node *emRoot;
		int count;
		explicit Trie(int c = 0) : count(c)
		{
			emRoot = new Node;
		}
		~Trie()
		{
			deleteTrie(emRoot);
		}
		void deleteTrie(Node *root)
		{
			if (root)
			{
				for (int i = 0; i < SIZE; i++)
				{
					deleteTrie(root->children[i]);
				}
				delete root;
				root = NULL;
			}
		}
	};

	void insert(Trie *trie, string &keys, string &keyWords)
	{
		int len = (int)keys.size();

		Node *pCrawl = trie->emRoot;
		trie->count++;

		for (int i = 0; i < len; i++)
		{
			int k = keys[i] - '0';
			if (!pCrawl->children[k])
			{
				pCrawl->children[k] = new Node;
			}
			pCrawl = pCrawl->children[k];
		}
		pCrawl->words.push_back(keyWords);
	}

	Node *search(Node *root, string &keys)
	{
		int len = (int)keys.size();

		Node *pCrawl = root;
		for (int i = 0; i < len; i++)
		{
			int k = keys[i] - '0';
			if (!pCrawl->children[k])
			{
				return NULL;//没走完所有keys
			}
			pCrawl = pCrawl->children[k];
		}
		return pCrawl;
	}

	void searchLeft(Node *leaf, Node *r,	int len, int &prun)
	{
		if (len >= prun) return;

		if (leaf->words.size())
		{
			r = leaf;
			prun = len;
			return;
		}

		for (int i = 0; i < SIZE; i++)
		{
			searchLeft(leaf->children[i], r, len+1, prun);
		}
	}

	void wordsToKey(string &keys, string &keyWords, 
		unordered_map<char, char> &umCC)
	{
		for (int i = 0; i < (int)keyWords.size(); i++)
		{
			keys.push_back(umCC[keyWords[i]]);
		}
	}

	void charsToMap(const string phdig[], unordered_map<char, char> &umCC)
	{
		for (int i = 0; i < 10; i++)
		{
			for (int k = 0; k < (int)phdig[i].size(); k++)
			{
				umCC[phdig[i][k]] = i + '0';
			}
		}
	}

	string searchComb(Trie *trie, string &num)
	{
		vector<string> tbl(num.size());
		for (int i = 0; i < (int)num.size(); i++)
		{
			string s = num.substr(0, i+1);
			Node *n = search(trie->emRoot, s);
			if (n && n->words.size())
			{
				tbl[i].append(n->words[0]);
				continue;//这里错误写成break!!
			}
			for (int j = 1; j <= i; j++)
			{
				if (tbl[j-1].size())
				{
					s = num.substr(j, i-j+1);
					n = search(trie->emRoot, s);
					if (n && n->words.size())
					{
						tbl[i].append(tbl[j-1]);
						tbl[i].append(" ");
						tbl[i].append(n->words[0]);
						break;
					}
				}
			}
		}

		if (tbl.back().size())
		{
			return tbl.back();
		}
		
		string ans;
		for (int i = 0; i < (int)tbl.size() - 1; i++)
		{
			if (tbl[i].size())
			{
				string tmp = tbl[i];
				string keys = num.substr(i+1);
				Node *n = search(trie->emRoot, keys);

				if (!n) continue;

				Node *r = NULL;
				int prun = INT_MAX;
				searchLeft(n, r, 0, prun);

				tmp += r->words[0];

				if (ans.empty() || tmp.size() < ans.size())
				{
					ans = tmp;
				}
			}
		}
		return ans.empty()? "No solution." : ans;
	}

	//测试函数,不使用解题
	void printTrie(Node *n)
	{
		if (n)
		{
			for (int i = 0; i < SIZE; i++)
			{
				printTrie(n->children[i]);	
				for (int j = 0; j < (int)n->words.size(); j++)
				{
					cout<<n->words[j]<<endl;
				}							
			}
		}
	}
public:
	PhoneNumber1002_2()
	{
		const string phdig[10] = 
		{"oqz","ij","abc","def","gh","kl","mn","prs","tuv","wxy"};
		unordered_map<char, char> umCC;
		charsToMap(phdig, umCC);

		int N;
		
		string num, keys, keyWords;
		while ((cin>>num) && "-1" != num)
		{
			cin>>N;

			Trie trie;
			while (N--)
			{
				cin>>keyWords;				
				wordsToKey(keys, keyWords, umCC);

				insert(&trie, keys, keyWords);

				keys.clear();//别忘记清空
			}

			cout<<searchComb(&trie, num)<<endl;
		}
	}
};



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值