Leetcode: Word Ladder II

给定起始和结束单词及字典,找到两者间所有最短变换序列,每次仅允许改变一个字母。本文介绍一种使用前驱单词表和双向队列模拟的方法来解决此问题,避免了层次信息丢失导致的去重困难。通过不断交换处理容器和待处理容器的角色,实现了从end出发找到所有路径。
摘要由CSDN通过智能技术生成

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

For example,

Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]

Return

  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]

Note:

  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
Time Limit Exceeded

vector<vector<string>> findLadders(string start, string end, unordered_set<string> &dict) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
		vector<vector<string>> res;
		if(start.size() != end.size()) return res;
		if(start.empty() || end.empty())return res;
		dict.insert(start);
		dict.insert(end);
		unordered_map<string,vector<string>> dictmp;
		set<string> visited;visited.clear();
		vector<string> path;//queue<string> path;
		path.push_back(start);//path.push(start);
		
		int count = 1;
		//dict.erase(start);
		bool found = false;
		while(dict.size() > 0 && !path.empty())
		{
			string curword = path.front();
			path.erase(path.begin());//path.pop();
			count--;
			visited.insert(curword);
			if(curword==end) {found = true;}
			for(int i = 0; i < curword.size(); i++)
			{
				string tmp = curword;
				for(char j='a'; j<='z'; j++)
				{
					if(curword[i]==j)continue;
					tmp[i] = j;
					if(dict.find(tmp) != dict.end() && visited.find(tmp) == visited.end())
					{
						path.push_back(tmp);//path.push(tmp);
						dictmp[curword].push_back(tmp);
					}
				}
			}
			if(count==0)
			{
				if(found)break;
				count = path.size();
				set<string>::iterator it=visited.begin();
				for(;it!=visited.end();it++)
					dict.erase(*it);
				visited.clear();
				for(int i =0; i < count; i++)
					visited.insert(path[i]);
			}
		}
		if(dictmp.size() == 0)return res;
		//Ladder
		vector<string> t;
		t.push_back(start);
		res.push_back(t);
		while(true)
		{
			vector<vector<string>>::iterator it = res.begin();
			string curword = (*it)[(*it).size()-1];
			while(it != res.end() && curword == end)
			{
				it++;
				if(it != res.end())
					curword = (*it)[(*it).size()-1];
				else
					break;
			}
			if(it == res.end())break;
			vector<string> tmp = *it;
			res.erase(it);
			vector<string> sec = dictmp[curword];
			for(int i=0; i<sec.size(); i++)
			{
				if(i!=0)
					tmp.pop_back();
				tmp.push_back(sec[i]);
				res.push_back(tmp);
			}
		}
		return res;
    }


使用了一个前驱单词表,即记录每一个单词的前驱单词是哪些。这样在遍历完毕后,我们从end出发递归就能把所有路径生成出来。但是由于前驱单词表不能记录当前的层次信息,似乎我们没法完成去重的工作。这个方案的巧妙之处就在于它没有使用我们通常的队列保存待处理的单词,一个单词一个单词先进先出处理的方法,而是使用两个vector来模拟队列的操作。我们从vector 1中遍历单词进行转换尝试,发现能转换的单词后将其放入vector 2中。当vector 1中的单词处理完毕后即为一层处理完毕,它里面的单词就可以从字典里删去了。接着我们对vector 2进行同样处理,如此反复直到当前处理的vector中不再有单词。我们发现vector 1和vector 2在不断地交换正处理容器和待处理容器的身份,因此可以通过将其放入一个数组中,每次循环对数组下标值取反实现身份的交替.

Accepted

class Solution {
public:
    vector<vector<string> > findLadders(string start, string end, unordered_set<string> &dict)
    {
        result_.clear();
        unordered_map<string, vector<string>> prevMap;
        for(auto iter = dict.begin(); iter != dict.end(); ++iter)
            prevMap[*iter] = vector<string>();
        vector<unordered_set<string>> candidates(2);
        int current = 0;
        int previous = 1;
        candidates[current].insert(start);
        while(true)
        {
            current = !current;
            previous = !previous;
            for (auto iter = candidates[previous].begin(); iter != candidates[previous].end(); ++iter)
                dict.erase(*iter);
            candidates[current].clear();
            
            for(auto iter = candidates[previous].begin(); iter != candidates[previous].end(); ++iter)
            {
                for(size_t pos = 0; pos < iter->size(); ++pos)
                {
                    string word = *iter;
                    for(int i = 'a'; i <= 'z'; ++i)
                    {
                        if(word[pos] == i)continue;
                        word[pos] = i;
                        if(dict.count(word) > 0)
                        {
                            prevMap[word].push_back(*iter);
                            candidates[current].insert(word);
                        }
                    }
                }
            }
            if (candidates[current].size() == 0)
                return result_;
            if (candidates[current].count(end)) break;
        }
        vector<string> path;
        GeneratePath(prevMap, path, end);
        return result_;
    }
    
private:
    void GeneratePath(unordered_map<string, vector<string>> &prevMap, vector<string>& path, const string& word)
    {
        if (prevMap[word].size() == 0)
        {
            path.push_back(word);
            vector<string> curPath = path;
            reverse(curPath.begin(), curPath.end());
            result_.push_back(curPath);
            path.pop_back();
            return;
        }
        path.push_back(word);
        for (auto iter = prevMap[word].begin(); iter != prevMap[word].end(); ++iter)
            GeneratePath(prevMap, path, *iter);
        path.pop_back();
    }
    vector<vector<string>> result_;
};


http://blog.csdn.net/niaokedaoren/article/details/8884938

http://www.cnblogs.com/shawnhue/archive/2013/06/05/leetcode_126.html



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值