Word Ladder

Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that:

  1. Only one letter can be changed at a time
  2. Each intermediate word must exist in the dictionary

For example,

Given:
start = "hit"
end = "cog"
dict = ["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.

public class Solution {
    public int ladderLength(String start, String end, Set<String> dict) {//字典中可能含有start和end
        //dict.add(start);
        //dict.add(end);
        String[] arr = (String[])dict.toArray(new String[0]); //将dict变成数组,方便记录每一个字典项转换成其他字典项需要的转换次数
        int[][] c = new int[arr.length][arr.length];//相当于邻接矩阵
        for(int i = 0 ; i < arr.length ; i ++){//构建图的邻接矩阵
            for(int j = i + 1 ; j < arr.length ; j ++){
                c[i][j] = count(arr[i],arr[j]);
                c[j][i] = c[i][j];//对称
            }
        }
        Set<Integer> set = new HashSet<Integer>();//存储到end的转换次数为1的节点
        for(int i = 0; i < arr.length ; i ++){
            if(count(arr[i],end) == 1 || end.equals(arr[i])){//相等的也要纳入其中
                set.add(i);
            }
        }
        //利用邻接矩阵找出两个点之间的最短通路
        int len = 0;
        int level = 0;
        Queue<Integer> queue = new LinkedList<Integer>();
        int[] visited = new int[arr.length];
        for(int i = 0 ; i < arr.length ; i ++){
            if(count(start,arr[i]) == 1 || start.equals(arr[i])){
                queue.offer(i);
                visited[i] = 1;
                level++;
            }
        }
        //visited[startIndex] = 1;
        while(queue.size() > 0){
            int count = 0;
            int nextLevel = 0;//下一层的元素数
            len ++;
            while(count < level){//第len层全部出队
                int tmp = queue.poll();
                if(set.contains(tmp)){
                    return len + 2;
                }
                for(int i = 0 ; i < arr.length ; i ++){
                    if(visited[i] != 1 && c[tmp][i] == 1){
                        queue.offer(i);
                        visited[i] = 1;
                        nextLevel++;
                    }
                }
                count ++;
            }
            level = nextLevel;
        }
        return 0;
    }
    public int  count(String s1, String s2){//字符串之间是否是一次交换
        int count = 0;
        for(int i = 0 ; i < s1.length(); i++){
            if(s1.charAt(i) != s2.charAt(i)){
                count++;
                if(count > 1){
                    break;
                }
            }
        }
        if(count == 1){//可以直达,
            return 1;
        }else{//不可直达
            return 0;
        }
    }
}

时间超时,查找原因,根据网上的说法,不是BFS的问题,而是构建图矩阵的时候(用时O(n^2))。。。。

继续度娘...........

有大神提出了另一种方案,不需要构造邻接矩阵,思路就是将字符串中的每一个位置用‘a’~‘z’都都换一次,然后在查看这个字符串是否在字典里,这样就等于找到了所有字典中与原字符串相差为1的字符串。然后就是利用BFS的思想搜索。


public class Solution {
    public int ladderLength(String start, String end, HashSet<String> dict) {  
        if (start == null || end == null || start.equals(end))  
            return 0;  
  
        if (isOneWordDiff(start, end))  
            return 2;  
  
        Queue<String> queue=new LinkedList<String>();//访问队列  
        Set<String> visited = new HashSet<String>();//表示字典里的对应key访问过了  
        int level = 0;//某一层的元素数
        int len = 0;
        //将能够进行一次转换就到字典里的string加到队列,也就是离start最近的
        StringBuilder sb = new StringBuilder(start);//重复修改字符串并且是单线程使用StringBuilder
        for(int i = 0 ; i < start.length(); i++){
            char tmp = sb.charAt(i);
            for(char j = 'a' ; j <= 'z' ; j ++){
                if(j == tmp){
                    continue;
                }else{
                    sb.setCharAt(i,j);
                    if(dict.contains(sb.toString())){
                        queue.offer(sb.toString());
                        level ++;
                        visited.add(sb.toString());
                    }
                }
            }
            sb.setCharAt(i,tmp);
        }
        while(!queue.isEmpty())  
        {  
            len++;
            int count = 0;
            int nextLevel = 0;
            while(count < level){//控制出队的个数
                String tmp = queue.poll();
                if(isOneWordDiff(tmp,end)){
                    return len + 2;
                }else{
                    sb = new StringBuilder(tmp);
                    for(int i = 0 ; i < tmp.length(); i++){
                        char c = sb.charAt(i);
                        for(char j = 'a' ; j <= 'z' ; j ++){
                            if(j == c){
                                continue;
                            }else{
                                sb.setCharAt(i,j);
                                if(dict.contains(sb.toString()) && !visited.contains(sb.toString())){
                                    queue.offer(sb.toString());
                                    nextLevel ++;
                                    visited.add(sb.toString());
                                }
                            }
                        }
                        sb.setCharAt(i,c);
                    }
                }
                count++;
            }
            level = nextLevel;
        }
        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)  
                    break;  
            }  
        }  
        return diff == 1;  
    }
}

Runtime:  754 ms

继续精简代码,在上一个算法,并没有将start和end加入字典,而且字典中有跟start和end相同的字符串出现,而字典中的这些start和end是没有作用的,白白浪费了26*length*2的时间。这次将start和end都加入字典中。

public class Solution {
    public int ladderLength(String start, String end, HashSet<String> dict) {  
        if (start == null || end == null)  
            return 0;  
  
        if (isOneWordDiff(start, end))  
            return 2;
        dict.add(start);//即使字典已经包含start和end也无所谓
        dict.add(end);
        
        Queue<String> queue=new LinkedList<String>();//访问队列  
        Set<String> visited = new HashSet<String>();//表示字典里的对应key访问过了  
        StringBuilder sb = new StringBuilder(start);//重复修改字符串并且是单线程使用StringBuilder
        
        int level = 0;//某一层的元素数
        int len = 0;
        queue.offer(start);
        level++;
        visited.add(start);
        while(!queue.isEmpty())  
        {  
            len++;
            int count = 0;
            int nextLevel = 0;
            while(count < level){//控制出队的个数
                String tmp = queue.poll();
                if(tmp.equals(end)){
                    return len;
                }else{
                    //将能够进行一次转换就到字典里的string加到队列,也就是离tmp最近的
                    sb = new StringBuilder(tmp);//重复修改字符串并且是单线程使用StringBuilder
                    for(int i = 0 ; i < tmp.length(); i++){
                        char c = sb.charAt(i);
                        for(char j = 'a' ; j <= 'z' ; j ++){
                            if(j == c){
                                continue;
                            }else{
                                sb.setCharAt(i,j);
                                if(dict.contains(sb.toString()) && !visited.contains(sb.toString())){
                                    queue.offer(sb.toString());
                                    nextLevel ++;
                                    visited.add(sb.toString());
                                }
                            }
                        }
                        sb.setCharAt(i,c);
                    }
                }
                count++;
            }
            level = nextLevel;
        }
        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)  
                    break;  
            }  
        }  
        return diff == 1;  
    }
}

Runtime: 662 ms

终于完成了,用了一天的时间,想法花费的时间还行,就是实现起来,各种问题接踵而来,最无奈的就是跟字符串打交道,太麻烦了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值