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总结相关推荐

  1. PHP第五周答案,算法设计与分析第五周作业——Word Ladder

    算法设计与分析第五周作业--Word Ladder 上周找了一道深度搜索优先搜索的算法题来做,于是这周就选了一道广度优先搜索算法题来试试手. 本周所选题目:原题目链接 题目详情 题目大意:给出一个字符 ...

  2. 【难点+重点BFS】LeetCode 126. Word Ladder II

    LeetCode 126. Word Ladder II Solution1: 参考自花花酱:http://zxi.mytechroad.com/blog/searching/leetcode-126 ...

  3. 【重点BFS】LeetCode 127. Word Ladder

    LeetCode 127. Word Ladder Solution1:我的超过40%的AC的答案 原先利用BFS做但是内存溢出未能AC:进过修改加上了标记是否访问过的visited数组,可以AC啦~ ...

  4. LeetCode 127. Word Ladder

    原题链接在这里:https://leetcode.com/problems/word-ladder/ 题目: Given two words (beginWord and endWord), and ...

  5. 算法细节系列(20):Word Ladder系列

    算法细节系列(20):Word Ladder系列 详细代码可以fork下Github上leetcode项目,不定期更新. 题目摘自leetcode: 1. Leetcode 127: Word Lad ...

  6. 33 单词接龙(Word Ladder)

    文章目录 1 题目 2 解决方案 2.1 思路 2.2 图解 2.2 时间复杂度 2.3 空间复杂度 3 源码 1 题目 题目:单词接龙(Word Ladder) 描述:给出两个单词(start和en ...

  7. Word Ladder

    LeetCode[127. Word Ladder] 题解 难度[medium] 题目: Given two words (beginWord and endWord), and a dictiona ...

  8. Leetcode的word ladder问题

    127. Word Ladder 题意如下: 给定一个开始字符串和结束字符串以及含有大量字符串的字典,要求通过一定规则,能从开始字符串变换到结束字符串,求其最小变换字符串数目,该规则如下: 1 从开始 ...

  9. Word Ladder系列

    1.Word Ladder 问题描述: 给两个word(beginWord和endWord)和一个字典word list,找出从beginWord到endWord之间的长度最长的一个序列,条件: 1. ...

  10. 127. Word Ladder 单词接龙

    给定两个单词(beginWord 和 endWord)和一个字典,找到从 beginWord 到 endWord 的最短转换序列的长度.转换需遵循如下规则: 每次转换只能改变一个字母. 转换过程中的中 ...

最新文章

  1. cmake 常用变量和常用环境变量查表手册
  2. 自己添加ODBC数据源的Access驱动
  3. 友商逼急 雷急跳墙:生死看淡 不服就干
  4. sudo: /usr/lib/sudo/sudoers.so must be owned by uid 0
  5. 家的味道,家的感觉!!!
  6. 获取当前登录的QQ号码
  7. JAVA 第1课 开始前的准备
  8. java线程系列---读写锁ReentrantReadWriteLock
  9. 变量unset与null的区别
  10. 设备树解析过程及platform设备注册
  11. 订单系统管理系统 开源_8个开源销售点系统
  12. 利用python的pyqt5和vtk库实现对gcode模型的全彩预览
  13. 联合概率分布、边缘概率分布
  14. f1c100A运行linux,荔枝派 Nano 全志 F1C100s 编译运行 Linux 笔记
  15. android 动态库符号表,Android NDK隐藏jni动态库的内部符号表
  16. 过孔在覆铜后不出现十字孔
  17. uis向3d投影全息界面的连续演化。
  18. obs代码总体架构图
  19. kafka comsumer消费消息后不commit offset的情况分析
  20. Android Service(一) Service初识

热门文章

  1. CyberLink ColorDirector Ultra(视频调色工具)中文版
  2. spring batch (四) Job的配置及配置文件说明介绍
  3. WinForm -- 为TextBox文本框添加鼠标右键菜单
  4. .NET MVC 学习笔记(一)— 新建MVC工程
  5. 2019年1月29日
  6. postgresql安装指南
  7. Spring MVC零配置(全注解)(版本5.0.7)
  8. 3、Python字典集合
  9. 获取android的SDK或者手机目录路径
  10. 团体程序设计天梯赛-练习集L1-013. 计算阶乘和