word-ladder总结
title: word ladder总结
categories: LeetCode
tags:
- 算法
- LeetCode
comments: true
date: 2016-10-16 09:42:30
---
题意
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
Note:
Return 0 if there is no such transformation sequence.
All words have the same length.
All words contain only lowercase alphabetic characters.
思路
因为上一个词到下一个词之间,变化的只是一个字母,因此只需遍历所有字母,进行替换即可;
利用BFS的思想,将改变后的字符串存进队列中,进行下一次的循环。
题解
这个的题解有很多种,但是其实都是使用了最基本的BFS思想;
交换队列,计算层数
int ladderLength(string begin, string end, unordered_set<string>& wordList) {// 当前层,下一层queue<string> current, next;// 判重unordered_set<string> visited;// 层次int level = 0;bool found = false;// 有点像内联函数auto state_is_target = [&](const string &s) {return s == end;};auto state_extend = [&](const string &s) {vector<string> result;for (size_t i = 0; i < s.size(); ++i) {string new_word(s);for (char c = 'a'; c <= 'z'; c++) {if (c == new_word[i]) continue;swap(c, new_word[i]);if (wordList.count(new_word) > 0 && !visited.count(new_word)) {result.push_back(new_word);visited.insert(new_word);}swap(c, new_word[i]);}}return result;};current.push(begin);while (!current.empty() && !found) {++level;while (!current.empty() && !found) {const string str = current.front();current.pop();const auto& new_states = state_extend(str);for (const auto& state : new_states) {next.push(state);if (state_is_target(state)) {//找到found = true;break;}}}swap(next, current);}if (found) return level + 1;else return 0;
}
总结:
使用了匿名函数,其中命名的state_is_target,state_extend是函数模版,分别做的操作就是判断字符串是否相等,返回字符串BFS遍历的下一层的数组;
同时用了两层循环,外循环用于计算层数和交换队列,内循环用于计算找到结尾字符串;
一个队列
/*** 改变单词中的字母, 查看是否存在** @param word <#word description#>* @param wordDict <#wordDict description#>* @param toVisit <#toVisit description#>*/
void addNextWords(string word, unordered_set<string>& wordDict, queue<string>& toVisit) {// 删除wordDict中的word,因为相同的没有用wordDict.erase(word);for (int i = 0; i < (int)word.length(); i++) {// 记录下word的字母char letter = word[i];// 变换word中每个字母,一个一个字母的查找是否匹配,因为只能替换一个字母for (int j = 0; j < 26; j++) {word[i] = 'a' + j;// 找到后加入访问过的队列,并从wordDict中删除if (wordDict.find(word) != wordDict.end()) {toVisit.push(word);wordDict.erase(word);}}// 变换回去word[i] = letter;}
}
/*** 使用BFS** @param beginWord <#beginWord description#>* @param endWord <#endWord description#>* @param wordDict <#wordDict description#>** @return <#return value description#>*/
int ladderLength2(string beginWord, string endWord, unordered_set<string>& wordDict) {// 加载最后的单词wordDict.insert(endWord);queue<string> toVisit;addNextWords(beginWord, wordDict, toVisit);// 从2开始,是因为需要经历开始和结尾的两个字母int dist = 2;// toVisit中存储的是通过改变一个字母就可以到达wordDict中存在的字符串while (!toVisit.empty()) {int num = (int)toVisit.size();for (int i = 0; i < num; i++) {string word = toVisit.front();toVisit.pop();if (word == endWord) return dist;addNextWords(word, wordDict, toVisit);}dist++;}return 0;
}
总结:
同样是计算BFS的层数;
两个指针
/*** 使用两个指针和BFS** @param beginWord <#beginWord description#>* @param endWord <#endWord description#>* @param wordDict <#wordDict description#>** @return <#return value description#>*/
int ladderLength3(string beginWord, string endWord, unordered_set<string>& wordDict) {unordered_set<string> head, tail, *phead, *ptail;head.insert(beginWord);tail.insert(endWord);int dist = 2;// 将phead指向更小size的unordered_set以便减少运行时间while (!head.empty() && !tail.empty()) {if (head.size() < tail.size()) {phead = &head;ptail = &tail;}else {phead = &tail;ptail = &head;}unordered_set<string> temp;for (auto iterator = phead->begin(); iterator != phead->end(); iterator++) {string word = *iterator;wordDict.erase(word);// 原理和上面一样,更换其中的字符,for (int i = 0; i < (int)word.length(); i++) {char letter = word[i];for (int k = 0; k < 26; k++) {word[i] = 'a' + k;if (ptail->find(word) != ptail->end())return dist;if (wordDict.find(word) != wordDict.end()) {temp.insert(word);wordDict.erase(word);}}word[i] = letter;}}dist++;swap(*phead, temp);}return 0;
}
总结:
两个指针指向了unordered_set的head和tail,其做法和第一种方法是相通的,只不过一个用了unordered_set,同时在循环前面还加上了根据数组大小判断指针指向不同的数组;
使用类
class WordNode {
public:string word;int numSteps;public:WordNode(string w, int n) {this->word = w;this->numSteps = n;}
};
/*** 使用类去处理记录数据,和前面的思想是类似的** @param beginWord <#beginWord description#>* @param endWord <#endWord description#>* @param wordDict <#wordDict description#>** @return <#return value description#>*/
int ladderLength4(string beginWord, string endWord, unordered_set<string>& wordDict) {queue<WordNode*> queue;queue.push(new WordNode(beginWord, 1));wordDict.insert(endWord);while (!queue.empty()) {WordNode * top = queue.front();queue.pop();string word = top->word;if (word == endWord) {return top->numSteps;}for (int i = 0; i < word.length(); i++) {char temp = word[i]; //先记录后面还有改回来for (char c = 'a'; c <= 'z'; c++) {if (temp != c) {word[i] = c;}if (wordDict.find(word) != wordDict.end()) {queue.push(new WordNode(word, top->numSteps + 1));wordDict.erase(word);}}word[i] = temp;}}return 0;
}
总结:
使用类去记录每个字符串的单前遍历的层数,本质还是计算层数,只不过用类去保存,这样的好处就是减少了一些计算代码;
题意
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the word list
For example,
Given:
beginWord = "hit"
endWord = "cog"
wordList = ["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.
思路
肯定需要两个数组,一个用来存储当前已经存在的字符串,一个用来存放遍历结果的字符串,最后两者进行交换,直到遍历当前数组为空;还有一个重点在于可以利用邻接矩阵的思想,用map存储字符串和数组一一对应关系,最后根据顺序进行输出map即可;
代码
vector<string> temp_path;
vector<vector<string>> result_path;/*** 生成路径(从后往前)** @param unordered_map<string <#unordered_map<string description#>* @param path <#path description#>* @param start <#start description#>* @param end <#end description#>*/
void generatePath(unordered_map<string, unordered_set<string>>& path, const string& start, const string& end) {temp_path.push_back(start);if (start == end) {vector<string> ret = temp_path;reverse(ret.begin(), ret.end());result_path.push_back(ret);return ;}for (auto it = path[start].begin(); it != path[start].end(); it++) {generatePath(path, *it, end);temp_path.pop_back();}
}/***** @param beginWord <#beginWord description#>* @param endWord <#endWord description#>* @param wordList <#wordList description#>** @return <#return value description#>*/
vector<vector<string>> findLadders(string beginWord, string endWord, unordered_set<string> &wordList) {temp_path.clear();result_path.clear();unordered_set<string> current_step;unordered_set<string> next_step;unordered_map<string, unordered_set<string>> path;unordered_set<string> unvisited = wordList; // 记录还没有访问过的结点if (unvisited.count(beginWord) > 0)unvisited.erase(beginWord);// 未访问过的值增加结尾字符串,开始处加入起点字符串unvisited.insert(endWord);current_step.insert(beginWord);while (current_step.count(endWord) == 0 && unvisited.size() > 0) {// 遍历当前层的所有元素for (auto cur = current_step.begin(); cur != current_step.end(); cur++) {string word = *cur;// 给字符串中的每个字符都替换字母for (int i = 0; i < beginWord.size(); i++) {for (int j = 0; j < 26; j++) {string tmp = word;// 相等情况下if (tmp[i] == 'a' + j) {continue;}tmp[i] = 'a' + j;// 表明字典中存在该字符串,可以到达下一步if (unvisited.count(tmp) > 0) {next_step.insert(tmp);path[tmp].insert(word);}}}}// 表明下一层中已经不存在任何没有访问过的if (next_step.empty()) {break;}// 清除所有没有访问过的值for (auto it = next_step.begin(); it != next_step.end(); it++) {unvisited.erase(*it);}current_step = next_step;next_step.clear();}// 存在结果if (current_step.count(endWord) > 0) {generatePath(path, endWord, beginWord);}return result_path;
}/*** 建立树** @param forward <#forward description#>* @param backward <#backward description#>* @param dict <#dict description#>* @param unordered_map<string <#unordered_map<string description#>* @param tree <#tree description#>* @param reversed <#reversed description#>** @return <#return value description#>*/
bool buildTree(unordered_set<string> &forward, unordered_set<string> &backward, unordered_set<string> &dict, unordered_map<string, vector<string> > &tree, bool reversed)
{if (forward.empty()) return false;// 如果开头的长度比结尾长,则从结尾开始找到开头if (forward.size() > backward.size())return buildTree(backward, forward, dict, tree, !reversed);// 存在开头和结尾则删除for (auto &word: forward) dict.erase(word);for (auto &word: backward) dict.erase(word);unordered_set<string> nextLevel; // 存储树的下一层bool done = false; //是否进一步搜索for (auto &it: forward) //从树的当前层进行遍历{string word = it;for (auto &c: word){char c0 = c; //存储初始值for (c = 'a'; c <= 'z'; ++c){if (c != c0){// 说明找到最终结果if (backward.count(word)){done = true; //找到!reversed ? tree[it].push_back(word) : tree[word].push_back(it); //keep the tree generation direction consistent;}// 说明还没找到最终结果但是在字典中找到else if (!done && dict.count(word)){nextLevel.insert(word);!reversed ? tree[it].push_back(word) : tree[word].push_back(it);}}}c = c0; //restore the word;}}return done || buildTree(nextLevel, backward, dict, tree, reversed);
}
/*** 生成路径** @param beginWord <#beginWord description#>* @param endWord <#endWord description#>* @param unordered_map<string <#unordered_map<string description#>* @param tree <#tree description#>* @param path <#path description#>* @param paths <#paths description#>*/
void getPath(string &beginWord, string &endWord, unordered_map<string, vector<string> > &tree, vector<string> &path, vector<vector<string> > &paths) //using reference can accelerate;
{if (beginWord == endWord) paths.push_back(path); //till the end;else{for (auto &it: tree[beginWord]){path.push_back(it);getPath(it, endWord, tree, path, paths); //DFS retrieving the path;path.pop_back();}}
}vector<vector<string>> findLadders2(string beginWord, string endWord, unordered_set<string> &dict) {vector<vector<string> > paths;vector<string> path(1, beginWord);if (beginWord == endWord){paths.push_back(path);return paths;}// 分别存储开头和结尾unordered_set<string> forward, backward;forward.insert(beginWord);backward.insert(endWord);unordered_map<string, vector<string> > tree;// 用来判断是从开头找到结尾还是从结尾找到开头,因为会从长度短的一方开始进行查找bool reversed = false;if (buildTree(forward, backward, dict, tree, reversed))getPath(beginWord, endWord, tree, path, paths);return paths;
}bool test() {unordered_set<string> words = {"hot","dot","dog","lot","log"};string start = "hit", end = "cog";vector<vector<string>> result = findLadders2(start, end, words);for (auto i = 0; i < result.size(); ++i) {for (auto j = 0; j < result[0].size(); ++j) {cout << result[i][j] << "->";}cout << endl;}return true;
}
这两种方法其实是一样的;
转载于:https://www.cnblogs.com/George1994/p/6399868.html
word-ladder总结相关推荐
- PHP第五周答案,算法设计与分析第五周作业——Word Ladder
算法设计与分析第五周作业--Word Ladder 上周找了一道深度搜索优先搜索的算法题来做,于是这周就选了一道广度优先搜索算法题来试试手. 本周所选题目:原题目链接 题目详情 题目大意:给出一个字符 ...
- 【难点+重点BFS】LeetCode 126. Word Ladder II
LeetCode 126. Word Ladder II Solution1: 参考自花花酱:http://zxi.mytechroad.com/blog/searching/leetcode-126 ...
- 【重点BFS】LeetCode 127. Word Ladder
LeetCode 127. Word Ladder Solution1:我的超过40%的AC的答案 原先利用BFS做但是内存溢出未能AC:进过修改加上了标记是否访问过的visited数组,可以AC啦~ ...
- LeetCode 127. Word Ladder
原题链接在这里:https://leetcode.com/problems/word-ladder/ 题目: Given two words (beginWord and endWord), and ...
- 算法细节系列(20):Word Ladder系列
算法细节系列(20):Word Ladder系列 详细代码可以fork下Github上leetcode项目,不定期更新. 题目摘自leetcode: 1. Leetcode 127: Word Lad ...
- 33 单词接龙(Word Ladder)
文章目录 1 题目 2 解决方案 2.1 思路 2.2 图解 2.2 时间复杂度 2.3 空间复杂度 3 源码 1 题目 题目:单词接龙(Word Ladder) 描述:给出两个单词(start和en ...
- Word Ladder
LeetCode[127. Word Ladder] 题解 难度[medium] 题目: Given two words (beginWord and endWord), and a dictiona ...
- Leetcode的word ladder问题
127. Word Ladder 题意如下: 给定一个开始字符串和结束字符串以及含有大量字符串的字典,要求通过一定规则,能从开始字符串变换到结束字符串,求其最小变换字符串数目,该规则如下: 1 从开始 ...
- Word Ladder系列
1.Word Ladder 问题描述: 给两个word(beginWord和endWord)和一个字典word list,找出从beginWord到endWord之间的长度最长的一个序列,条件: 1. ...
- 127. Word Ladder 单词接龙
给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度.转换需遵循如下规则: 每次转换只能改变一个字母. 转换过程中的中 ...
最新文章
- cmake 常用变量和常用环境变量查表手册
- 自己添加ODBC数据源的Access驱动
- 友商逼急 雷急跳墙:生死看淡 不服就干
- sudo: /usr/lib/sudo/sudoers.so must be owned by uid 0
- 家的味道,家的感觉!!!
- 获取当前登录的QQ号码
- JAVA 第1课 开始前的准备
- java线程系列---读写锁ReentrantReadWriteLock
- 变量unset与null的区别
- 设备树解析过程及platform设备注册
- 订单系统管理系统 开源_8个开源销售点系统
- 利用python的pyqt5和vtk库实现对gcode模型的全彩预览
- 联合概率分布、边缘概率分布
- f1c100A运行linux,荔枝派 Nano 全志 F1C100s 编译运行 Linux 笔记
- android 动态库符号表,Android NDK隐藏jni动态库的内部符号表
- 过孔在覆铜后不出现十字孔
- uis向3d投影全息界面的连续演化。
- obs代码总体架构图
- kafka comsumer消费消息后不commit offset的情况分析
- Android Service(一) Service初识