题目描述
给定两个单词(beginWord
和 endWord
)和一个字典,找到从 beginWord
到 endWord
的最短转换序列的长度。转换需遵循如下规则:
- 1、每次转换只能改变一个字母。
- 2、转换过程中的中间单词必须是字典中的单词。
说明:
- 如果不存在这样的转换序列,返回 0。
- 所有单词具有相同的长度。
- 所有单词只由小写字母组成。
- 字典中不存在重复的单词。
- 你可以假设
beginWord
和endWord
是非空的,且二者不相同。
样例
输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]
输出: 5
解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
返回它的长度 5。
输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]
输出: 0
解释: endWord "cog" 不在字典中,所以无法进行转换。
算法1
单向广搜
- 1、用哈希表记录每个单词到起点
beginWord
的最短距离 - 2、从起点位置标记长度是
1
,从起点beginWord
开始出发,若下一个单词word
能和队列poll
出当前的单词s
连上一条边,且哈希表中不存在,则更新下一个单词word
的到起点的距离dist[word] = dist[s] + 1
,并加入到队列中 - 3、最后返回
dist[endWord]
时间复杂度 $O(n^2L)$
Java 代码
class Solution {
static boolean check(String a,String b)
{
int n = a.length();
int res = 0;
for(int i = 0;i < n;i ++)
{
if(a.charAt(i) != b.charAt(i)) res ++;
}
return res == 1;
}
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
HashMap<String, Integer> dist = new HashMap<String, Integer>();
dist.put(beginWord,1);
Queue<String> q = new LinkedList<String>();
q.add(beginWord);
while(!q.isEmpty())
{
String t = q.poll();
for(String word : wordList)
{
if(check(t, word) && dist.get(word) == null)
{
dist.put(word, dist.get(t) + 1);
q.add(word);
if(word.equals(endWord)) return dist.get(word);
}
}
}
return 0;
}
}
算法2
双向广搜
- 1、
da
存储的是单词到起点beginWord
的最短距离,db
存的是单词到终点endWord
的最短距离,初始化da[beginWord] = 1
,db[endWord] = 1
- 2、
qa
存储的是当前从起点begintWord
出发枚举到的某一层的所有单词,qb
存储的是当前从终点endWord
出发枚举到的某一层的所有单词 - 3、从起点
beginWord
和 终点endWord
同时向外一层层搜索,选择qa
和qb
中单词量最少的队列进行扩展,当poll
出的单词s
并搜索下一个单词word
时,假设poll
的单词是qa
的,则若该单词word
在另一遍db
存储中存在,则表示可以通过连接 单词s
和单词word
,完成起点beginWord
到终点endWord
的连接,直接返回dist[s] + dist[word]
注意:从某个队列开始poll
元素时,必须把队列中的所有元素(某一层的元素)poll
出来进行扩展下一层,若按照一个元素poll
出后继续比较队列大小再次选择poll
哪个队列的元素,会容易造成qa
存储的是多于两层的元素,qb
存储多余两层的元素,会对最短距离造成影响
时间复杂度 $O(n^2L)$
Java 代码
class Solution {
static int INF = 0x3f3f3f3f;
static boolean check(String a,String b)
{
int n = a.length();
int res = 0;
for(int i = 0;i < n;i ++)
{
if(a.charAt(i) != b.charAt(i)) res ++;
}
return res == 1;
}
static int extend(HashMap<String, Integer> da, HashMap<String, Integer> db, Queue<String> qa, Queue<String> qb, List<String> wordList)
{
int n = qa.size();
for (int i = 0; i < n; i++) {
String s = qa.poll();
for(String word : wordList)
{
if(check(s, word) && !da.containsKey(word))
{
if(db.containsKey(word)) return da.get(s) + db.get(word);
da.put(word, da.get(s) + 1);
qa.add(word);
}
}
}
return INF;
}
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
boolean flag = false;
for(String word : wordList)
if(word.equals(endWord))
{
flag = true;
break;
}
if(!flag) return 0;
HashMap<String, Integer> da = new HashMap<String, Integer>();
HashMap<String, Integer> db = new HashMap<String, Integer>();
da.put(beginWord,1);
db.put(endWord,1);
Queue<String> qa = new LinkedList<String>();
Queue<String> qb = new LinkedList<String>();
qa.add(beginWord);
qb.add(endWord);
while(!qa.isEmpty() && !qb.isEmpty())
{
int t = 0;
if(qa.size() <= qb.size()) t = extend(da, db, qa, qb, wordList);
else t = extend(db, da, qb, qa, wordList);
if(t != INF)
{
return t;
}
}
return 0;
}
}
这个注意真的厉害,如果没有将这一层的全部
poll
出来,最后一个样例不能通过但是
qb存储多余两层的元素,会对最短距离造成影响
这是为啥会有影响?老哥能不能给我示范一下acwing1102的双向广搜咋写呀,总是写不好哈哈
能帮我看一下我写的这个哪里效率低吗,超时了
感觉没什么错,可能是map的问题,也可能是string构造的次数太多
感谢小呆呆老师
有这么几点建议供参考:
1. unorder_map改为map试试,前者最坏情况会退化为O(n)
2. string的构造次数太多
3. x.count()的复杂度是O(n)的,前面改为map后可以用[]来引用,稳定log级别的,或者采用数组记忆化的方式,像我开个二维数组那样
4. swap()的复杂度不是很了解,如果是O(n)的话就很糟糕了,可以通过记录队列大小的方式把这个地方优化掉
暂且改进下这几点试试吧
建议不要一开始就重度依赖STL,先明确相应操作的复杂度会是你更好的利用STL
谢谢, 查找某个值的时候unordered_map效率好像比map要高吧, 如果不构造string 改成 map[HTML_REMOVED]> hash 这样排除重复还是过不了, swap复杂度我也不是很了解不过LeetCode上好几个题双向BFS 我都用的swap,都是速度100%, count那个可能有问题我打算考完试之后再弄弄哈哈