对于之前没有接触过该类型题目的人来说,此题无疑是个难题,本人提交了10次才正确通过,期间遇到了非常多的问题,感觉几乎把OJ的所有错误遍历了一遍,下面详细说说自己做该题的经验。

首先承认,我一开始并没有想到什么图模型,或者说是一点思路都没有。然后我就冥思苦想,首先想到了可以先构造一个二维矩阵,判断给定的词之间是否能两两一步到达,这一步可以通过两层循环加字符串的遍历完成,应该不难。获得这个矩阵之后,我居然还是没有想到图,只是在纸上写写画画感觉问题可以转化成在矩阵中搜索一条路径可以到达最后一行。路径搜索应该可以通过深搜完成,之后就开始按照这个思路写代码,结果越写越乱,遇到多次MLE。

多次尝试无果后,我感觉之前的思路不对,需要重整思路。然后又是写写画画,突然灵光一闪:搜索路径貌似可以用图表示!然后按照这个思路很快就想明白了整个流程:首先构造一个无向图用来表示单词之间的可达性,然后从表示起点的节点开始对整个图进行BFS遍历,直到找到表示end的节点。按照这个思路也很快写出代码,但是提交后又遇到新问题:不停地超时。此时说明算法已经正确,但是复杂度还是太高。不妨先看一下这时的代码:

public int ladderLength2(String start, String end, HashSet<String> dict) {
if (start == null || end == null || start.equals(end)
|| start.length() != end.length())
return 0; if (isOneWordDiff(start, end))
return 2; dict.add(start);
dict.add(end); String[] dicts = (String[]) dict.toArray(new String[0]); int size = dicts.length;   // 表示图的列表数组
ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>(); int s = 0, e = 0;
// 判断一个单词一步可以到达哪些单词
for (int i = 0; i < size; i++) {
if (start.equals(dicts[i]))
s = i;
if (end.equals(dicts[i]))
e = i; ArrayList<Integer> list = new ArrayList<Integer>();
for (int j = 0; j < size; j++) {
if (isOneWordDiff(dicts[i], dicts[j]))
list.add(j);
} lists.add(list);
} HashMap<Integer,Integer> dist = new HashMap<Integer, Integer>(); Queue<Integer> queue = new LinkedList<Integer>();
queue.add(s);
dist.put(s, 1); while (!queue.isEmpty()) {
int index = queue.poll();
ArrayList<Integer> list = lists.get(index); for (int i = 0; i < list.size(); i++) {
if (list.get(i) == e) {
return dist.get(index) + 1;
} if (!dist.containsKey(list.get(i))) {
queue.add(list.get(i));
dist.put(list.get(i), dist.get(index) + 1);
}
}
} return 0;
}
private boolean isOneWordDiff(String a, String b) {
int diff = 0;
for (int i = 0; i < a.length(); i++) {
if (a.charAt(i) != b.charAt(i)) {
diff++;
if (diff >= 2)
return false;
}
} return diff == 1;
}

分析一下上面代码的思路:

1.首先将start和end添加到词典中,并将词典转化成数组;

2.创建一个二维数组,二维数组的每一维表示某个单词可以通过一步转化到达的单词,也即利用邻接表的方式存储图结构。在这个过程中也获得了start和end代表的数字;

3.利用构造的图进行BFS遍历,直到遇到end节点或者返回0。在遍历的过程中,由于图的边长是1,所以我们在遍历的时候总是得到从start到某个节点的最短路径,所以我们只需要考虑尚未遍历过的顶点即可。

上述代码是正确的,但是一直超时真是让人搞不清状况。后来,只能上网搜索别人的解答才明白其中的原因。超时的地方不在BFS遍历,而是在我们构造图的地方。我们采用了两层遍历构造一个图,此时复杂度是O(n2),数据量很小时可能体现不出它的劣势,但是当n上千时(给定的测试集中有这样的例子),上面的方法构造图就显得太慢。

这里我们可以不用实际构造图,而在BFS遍历的时候去寻找当前单词可达的下一个单词。如果还是通过遍历所有的单词判断是否可达,则复杂度和上面一样,但实际上在上千个单词中,只有少数几个可以由当前单词一步到达,我们之前的比较浪费了很多时间在不可能的单词上。网上对该问题的解决无一例外都是按照下面的思路:将当前单词每一个字符替换成a~z的任意一个字符,然后判断是否在词典中出现。此时的复杂度是O(26*word_length),当单词比较短时,这种方法的优势就体现出来了。按照这种思路修改后的代码如下:

public int ladderLength(String start, String end, HashSet<String> dict) {
if (start == null || end == null || start.equals(end)
|| start.length() != end.length())
return 0; if (isOneWordDiff(start, end))
return 2; Queue<String> queue=new LinkedList<String>();
HashMap<String,Integer> dist=new HashMap<String,Integer>(); queue.add(start);
dist.put(start, 1); while(!queue.isEmpty())
{
String head=queue.poll(); int headDist=dist.get(head);
//从每一个位置开始替换成a~z
for(int i=0;i<head.length();i++)
{
for(char j='a';j<'z';j++)
{
if(head.charAt(i)==j) continue; StringBuilder sb=new StringBuilder(head);
sb.setCharAt(i, j); if(sb.toString().equals(end)) return headDist+1; if(dict.contains(sb.toString())&&!dist.containsKey(sb.toString()))
{
queue.add(sb.toString());
dist.put(sb.toString(), headDist+1);
}
}
}
} return 0;
   }

我们可以看出,代码更加简洁,而且效率也更高,提交上述代码就可以AC了。

leetcode之word ladder的更多相关文章

  1. Java for LeetCode 126 Word Ladder II 【HARD】

    Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...

  2. [LeetCode] 126. Word Ladder II 词语阶梯 II

    Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformat ...

  3. [LeetCode] 127. Word Ladder 单词阶梯

    Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest t ...

  4. LeetCode 126. Word Ladder II 单词接龙 II(C++/Java)

    题目: Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transfo ...

  5. [Leetcode Week5]Word Ladder II

    Word Ladder II 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/word-ladder-ii/description/ Descripti ...

  6. [Leetcode Week5]Word Ladder

    Word Ladder题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/word-ladder/description/ Description Give ...

  7. [LeetCode] 126. Word Ladder II 词语阶梯之二

    Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformat ...

  8. 【leetcode】Word Ladder

    Word Ladder Total Accepted: 24823 Total Submissions: 135014My Submissions Given two words (start and ...

  9. 【leetcode】Word Ladder II

      Word Ladder II Given two words (start and end), and a dictionary, find all shortest transformation ...

  10. [Leetcode][JAVA] Word Ladder II

    Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from ...

随机推荐

  1. Java中Semaphore(信号量)的使用

    Semaphore的作用: 在java中,使用了synchronized关键字和Lock锁实现了资源的并发访问控制,在同一时间只允许唯一了线程进入临界区访问资源(读锁除外),这样子控制的主要目的是为了 ...

  2. Zookeeper核心工作机制(zookeeper特性、zookeeper数据结构、节点类型)

    10.1 zookeeper特性 1.Zookeeper:一个leader,多个follower组成的集群. 2.全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个ser ...

  3. Android开发过程中在sh,py,mk文件中添加log信息的方法

    Android开发过程中在sh,py,mk文件中添加log信息的方法 在sh文件中: echo "this is a log info" + $info 在py文件中: print ...

  4. APP自动化框架LazyAndroid使用手册(2)--元素自动抓取

    作者:黄书力 概述 前面的一篇博文简要介绍了安卓自动化测试框架LazyAndroid的组成结构和基本功能,本文将详细描述此框架中元素自动抓取工具lazy-uiautomaterviewer的使用方法. ...

  5. java集合循环删除

    java集合循环删除,java list集合操作,java循环.分享牛,分享牛原创.java集合删除方法. 2.6.1.第一种方式 list.add("1"); list.add( ...

  6. python模块collections中namedtuple()的理解

    Python中存储系列数据,比较常见的数据类型有list,除此之外,还有tuple数据类型.相比与list,tuple中的元素不可修改,在映射中可以当键使用.tuple元组的item只能通过index ...

  7. Android TV开发总结(三)构建一个TV app的焦点控制及遇到的坑

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52835829 前言:上篇中,&l ...

  8. Android开发之Path类使用详解,自绘各种各样的图形!

    玩过自定义View的小伙伴都知道,在View的绘制过程中,有一个类叫做Path,Path可以帮助我们实现很多自定义形状的View,特别是配合xfermode属性来使用的时候.OK,那我们今天就来看看P ...

  9. Apache shiro集群实现 (七)分布式集群系统下---cache共享

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  10. Dynamics CRM2016 Web API之Create related entities in one operation

    本篇继续来介绍两个web api的接口,一个是"Create related entities in one operation"即在一步操作中完成主实体的创建加关联实体的创建,一 ...