最小操作数

题目详情

给了A、B两个单词和一个单词集合Dict,每个的长度都相同。我们希望通过若干次操作把单词A变成单词B,每次操作可以改变单词中的一个字母,同时,新产生的单词必须是在给定的单词集合Dict中。求所有行得通步数最少的修改方法。


   举个例子如下:

Given:

   A = "hit"

   B = "cog"

   Dict = ["hot","dot","dog","lot","log"]

Return

 [

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

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

 ]


    即把字符串A = "hit"转变成字符串B = "cog",有以下两种可能:

  1. "hit" -> "hot" ->  "dot" ->  "dog" -> "cog";
  2. "hit" ->  "hot" ->  "lot" ->  "log"  ->"cog"。


        题目的基本思路是根据已有的单词构建一个图,然后从start开始用深度优先遍历所有的顶点,遍历到end记录遍历的路径。
     代码如下:
#include <string>
#include <vector>
#include <iostream>
#include <set>
#include <map>
#include <algorithm>
using namespace std;


class Solution
{
	
public:
	struct Node {
		int num;
		struct Node *next;
		Node() {
			next = NULL;
		}
	};

	struct Graph {
		Node **head; //链表头指针
		map<string, int> string2int;
		vector<string> int2string;

		bool isStep(const string& a, const string& b) {
			int cnt = 0;
			for (int i=0; i<a.length(); i++) {
				if (a[i] != b[i]) cnt ++;
				if (cnt > 1) return false;
			}
			return true;
		}

		void insertEdge(int ii, int jj) {
			Node *nd = new Node;
			nd->num = jj;
			nd->next = head[ii];
			head[ii] = nd;
		}

		Graph(const set<string> &dict) {
			head = new Node*[dict.size()];
			for (int i=0; i<dict.size(); i++) head[i] = NULL;
			int t = 0;
			for (set<string>::const_iterator it=dict.begin(); it != dict.end(); it++) {
				string2int[(*it)] = t++;
				int2string.push_back(*it);
			}

			for (int i=0; i<int2string.size(); i++) {
				for (int j=0; j<i; j++) {
					if (isStep(int2string[i], int2string[j])) {
						int ii = string2int[int2string[i]];
						int jj = string2int[int2string[j]];
						insertEdge(ii, jj);
						insertEdge(jj, ii);
					}
				}
			}
				
		}
		~Graph() {
			for (int i=0; i<int2string.size(); i++) {
				deleteHead(head[i]);
			}
			
		}

		void deleteHead(Node *nd) {
			if (nd != NULL) {
				if (nd->next)
                                    deleteHead(nd->next);
				delete nd;
			}
		}
		vector<vector<string>> ret;

		void dfs(int starti, const int endi, vector<string> &path) {
			visit[starti] = 1;
			path.push_back(int2string[starti]);
			if (starti == endi) {
				ret.push_back(path);
				path.pop_back();
				visit[starti] = 0;
				return;
			}
			
			for (Node *i=head[starti]; i!= NULL; i = i->next) {
				if (!visit[i->num]) dfs(i->num, endi, path);
			}
			path.pop_back();
			visit[starti] = 0;
		}
		vector<bool> visit;
		
		void getAns(string& start, const string& end) {
			int starti = string2int[start];
			int endi = string2int[end];

			visit.resize(int2string.size(), 0);
			vector<string> path;
			dfs(starti, endi, path);

		}

			
	};
public:
    vector<vector<string>> findLadders(string start, string end, set<string>& dict)
    {
		dict.insert(start);
		dict.insert(end);
        Graph *g = new Graph(dict);
		g->getAns(start, end);
		vector<vector<string> > temp = g->ret;
		int minStep = 0xfffffff;
		for (int i=0; i<temp.size(); i++) {
			if (temp[i].size() < minStep)
				minStep = temp[i].size();
		}
		vector<vector<string> > ret;
		for (int i=0; i<temp.size(); i++) {
			if (temp[i].size() == minStep)
				ret.push_back(temp[i]);
		}
		delete g;
		return ret;
    }
};


//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{   
	set<string> dict;
	dict.insert("hot");
	dict.insert("dot");
	dict.insert("dog");
	dict.insert("lot");
	dict.insert("log");
    string start = "hit";
	string end = "cog";
	Solution *s = new Solution;
	vector<vector<string> > v = s->findLadders(start, end, dict);
	delete s;
	
	for (int i=0; i<v.size(); i++) {
		for (int j=0; j<v[i].size(); j++) {
			cout << v[i][j] << " ";
		}
		cout << endl;
	}
	return 0;
} 
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。

由于一般深度优先搜索会搜索很多不必要的分支,可以在此基础上做一个剪枝。 下面我另外写了一个比较好的剪纸,具体的思路是先通过广度优先搜索,给图构建一个层次网络,记录每个节点记录源点的距离,然后在这个层次网络上从叶子节点开始进行dfs(从叶子节点开始很重要,可以省掉大部分节点)。
class Solution {
public:
void dfs(vector<vector<string> > &ret, map<string, int> &lay, string &cur, string &end, vector<string> &path, unordered_set<string> &dict) {
    if(cur == end) {
        ret.push_back(path);
        return;
    }
    
    for(int i=0; i<cur.length(); i++) {   
        for(int j=0; j<26; j++) {
            string t = cur;
            if(t[i] != 'a' + j) {
                t[i] = 'a' + j;
                if(dict.find(t) != dict.end() && lay[cur] == lay[t]+1) {
                    path.push_back(t);
                    dfs(ret, lay, t, end, path, dict);
                    path.pop_back();
                }
            }
        }
    }
}
vector<vector<string> > findLadders(string start, string end, unordered_set<string> &dict) {
    queue<string> q;
    map<string, int> lay;
    q.push(end);
    lay[end] = 0;
    while(!q.empty()) {
        string s = q.front();
        q.pop();
        for(int i=0; i<s.length(); i++) {
            string t = s;
            for(int j=0; j<26; j++) {
                if(t[i] != (char)('a' + j)) {
                    t[i] = (char)('a' + j);
                    if(dict.find(t) != dict.end() && lay.find(t) == lay.end()) {
                        lay[t] = lay[s] + 1;
                        q.push(t);
                    }
                }
            }
        }
    }
    
    vector<vector<string> > ret;
    vector<string> path;
    path.push_back(start);
    dfs(ret, lay, start, end, path, dict);
    return ret;
}
};




  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值