Word Ladder II

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) 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"]

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.

用了一天的时间终于把它AC掉了,不容易啊,太难了,也证明了自己的能力还是不行。总结一下这道题的思路:

基本的思路依然延续Word Ladder的思想,用‘a’~‘z’替换字符串中的每一个值来判断字符串和字典中字符串之间的关系,如果一次替换后的字符串出现字典中,那么说明这个字符串和替换后对应字典中字符串之间的距离为1,这样就节省了创建邻接矩阵的时间,这个还是一个很不错的想法。

其次就是本题的特色了,不仅要求出ladder的长度,还要知道ladder的每一阶是什么,也就是记录下搜索路径。记录搜索路径的话就是DFS了。如何知道路径呢,那么就要创建搜索树。如何创建搜索树呢?按照传统的BFS的思路,访问一个节点就将这个节点设置为visited的,那么当所有的节点都visited之后,这颗树就成了,那么是否有问题呢?用图说明问题



是否观察到区别?

传统的BFS搜索的节点只有一个确定的父节点,而我们需要的搜索树会出现多个父节点因此,传统的BFS的改良,我们使用两个队列来记录访问的过程,一个就是传统BFS算法所用的队列queue,然后这里还要使用一个额外的队列LevelElements,这个队列就是从上一层得到的下一层的元素,可以重复,如第二层是ted,rex,那么访问过这一层之后得到的LevelElements就是[tad,tex,tex],然后去重加入queue中。这就是改进的搜索树。

至于树节点之间的关系,我们使用一个map,记录访问到的每一个节点和节点的parent节点。

DFS过程中,由于已经有了每一个节点的parent节点,那么搜索树的形状就知道了,所以用DFS就可以得到所有的路径。至于路径的保存问题,我们使用一个LinkedList 类型tmp列表,搜索到一个节点,就将它加入tmp,离开它的时候从tmp中移除。这样当搜索到start的时候tmp就是一个完整的路径了,然后深度copy tmp,加入总路径记录的list中。

至此,整个算法思想介绍完毕了,算法是自己一边度娘一边自己写,花费的时间虽然很多,但是还是很高兴的,终于AC过了。

这道题DFS和BFS结合的还是很完美的。

public class Solution {
    public List<List<String>> findLadders(String start, String end, Set<String> dict) { 
        List<List<String>> list = new LinkedList<List<String>>();
        //特殊情况
        if (start == null || end == null)  
            return list;  
        if (isOneWordDiff(start, end)){
           List<String>  l = new ArrayList<String>();
           l.add(start);
           l.add(end);
           list.add(l);
           return list;
        }  
        
        dict.add(start);//即使字典已经包含start和end也无所谓
        dict.add(end);
        
        /*********主要涉及的数据结构***********/
        Queue<String> queue = new LinkedList<String>();//访问队列,用来记录访问的顺序,里面可能存在了多层的元素
	Queue<String> levelElements = new LinkedList<String>();//记录通过上层可以访问到的所有的元素,可能会有重复,这个队列只保存一层里的数据,不会保存多层元素
        Map<String,Set<String>> parent = new HashMap<String,Set<String>>();//使用map记录访问节点和访问节点的父节点,由于有可能有多个parent所以使用Set来存储
	Set<String> visited = new HashSet<String>();
        /************************************************/
        //List<String> parent = new ArrayList<String>();//存储end的parent
        StringBuilder sb = null;//重复修改字符串并且是单线程使用StringBuilder
        int level = 0;//某一层的元素数
        //int len = 0;
        queue.offer(start);
	visited.add(start);
        level++;
        parent.put(start,new HashSet<String>());//start的parent为""
        Set<String> parentSet = null;
        //parentSet.add("");
	//System.out.println(parent);
        boolean flag = false;//找到了end所在的len层,那么当len层的所有元素都出队之后就可以结束了
        while(!queue.isEmpty())  
        {  
            //len++;
            int count = 0;
            //int nextLevel = 0;
            while(count < level){//控制出队的个数,len层出队
                String tmp = queue.poll();
                if(end.equals(tmp)){//找到了end,如果找到了end,说明end的parent已经存到了map里,因此可以退出循环,而不用管是否队空
                    flag = true;
                    break;
                }else{
                    //将能够进行一次转换就到字典里的string加到队列,也就是离tmp最近的
                    sb = new StringBuilder(tmp);//重复修改字符串并且是单线程使用StringBuilder
                    for(int i = 0 ; i < tmp.length(); i++){
                        char c = sb.charAt(i);
                        //parentSet = parent.get(tmp);
                        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())){       
		     			levelElements.offer(sb.toString());			
                                        if(parent.containsKey(sb.toString())){
                                            parentSet = parent.get(sb.toString());
                                            parentSet.add(tmp);
                                        }else{
                                            //queue.offer(sb.toString());                   
                                            parentSet = new HashSet<String>();
                                            parentSet.add(tmp);
                                            parent.put(sb.toString(),parentSet);   
                                        }
                                }
                            }
                        }
                        sb.setCharAt(i,c);
                    }
                }
                count++;
            }
	    if(flag){
                break;
            }
	    level = 0;
	    //System.out.println(levelElements); 
	    while(!levelElements.isEmpty()){
	    	String tmp = levelElements.poll();
		if(!visited.contains(tmp)){
			queue.offer(tmp);
			level++;
			visited.add(tmp);
		}
	    }
	    //System.out.println(queue); 
	    //break;
        }
        if(!parent.containsKey(end)){
            return list;
        }
	//System.out.println(parent);
        List<String> tmp = new LinkedList<String>();//用来构造路径
        buildPath(list,tmp,parent,start,end);
        return list;
    }
    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;  
    }
    public void buildPath(List<List<String>> list,List<String> tmp,Map<String,Set<String>> parent,String start,String s){
        if(s.equals(start)){
		tmp.add(0,s);
                List<String> l = new LinkedList<String>();
                for(int i = 0; i < tmp.size(); i ++){//深度复制
                    l.add(tmp.get(i));
                }
		//l.add(0,start);
                list.add(l);
		tmp.remove(s);
	}
	Set<String> set = parent.get(s);//获得s的父亲
        for(String s1: set){
                tmp.add(0,s);
                buildPath(list,tmp,parent,start,s1);
                tmp.remove(s);
        }
    }
}



Runtime: 888 ms

这个运行时间挺不错的,提交了多次得到了这个时间大笑,时间不一定,有时候能到700多ms。

自己的测试代码

import java.util.*;
public class Solution {
    public static void main(String[] args){
    	Solution solution = new Solution();
	String start = "qa";
	String end = "sq";
	String[] s = {"si","go","se","cm","so","ph","mt","db","mb","sb","kr","ln","tm","le","av","sm","ar","ci","ca","br","ti","ba","to","ra","fa","yo","ow","sn","ya","cr","po","fe","ho","ma","re","or","rn","au","ur","rh","sr","tc","lt","lo","as","fr","nb","yb","if","pb","ge","th","pm","rb","sh","co","ga","li","ha","hz","no","bi","di","hi","qa","pi","os","uh","wm","an","me","mo","na","la","st","er","sc","ne","mn","mi","am","ex","pt","io","be","fm","ta","tb","ni","mr","pa","he","lr","sq","ye"};
	Set<String> dict = new HashSet<String>();
	for(int i = 0 ; i < s.length; i ++){
		dict.add(s[i]);
	}
	//System.out.println(solution.findLadders(start,end,dict));
	solution.findLadders(start,end,dict);
	//System.out.println(dict);
    }
    public List<List<String>> findLadders(String start, String end, Set<String> dict) { 
        List<List<String>> list = new LinkedList<List<String>>();
        //特殊情况
        if (start == null || end == null)  
            return list;  
        if (isOneWordDiff(start, end)){
           List<String>  l = new ArrayList<String>();
           l.add(start);
           l.add(end);
           list.add(l);
           return list;
        }  
        
        dict.add(start);//即使字典已经包含start和end也无所谓
        dict.add(end);
        
        /*********主要涉及的数据结构***********/
        Queue<String> queue = new LinkedList<String>();//访问队列,用来记录访问的顺序,里面可能存在了多层的元素
	Queue<String> levelElements = new LinkedList<String>();//记录通过上层可以访问到的所有的元素,可能会有重复,这个队列只保存一层里的数据,不会保存多层元素
        Map<String,Set<String>> parent = new HashMap<String,Set<String>>();//使用map记录访问节点和访问节点的父节点,由于有可能有多个parent所以使用Set来存储
	Set<String> visited = new HashSet<String>();
        /************************************************/
        //List<String> parent = new ArrayList<String>();//存储end的parent
        StringBuilder sb = null;//重复修改字符串并且是单线程使用StringBuilder
        int level = 0;//某一层的元素数
        //int len = 0;
        queue.offer(start);
	visited.add(start);
        level++;
        parent.put(start,new HashSet<String>());//start的parent为""
        Set<String> parentSet = null;
        //parentSet.add("");
	//System.out.println(parent);
        boolean flag = false;//找到了end所在的len层,那么当len层的所有元素都出队之后就可以结束了
        while(!queue.isEmpty())  
        {  
            //len++;
            int count = 0;
            //int nextLevel = 0;
            while(count < level){//控制出队的个数,len层出队
                String tmp = queue.poll();
                if(end.equals(tmp)){//找到了end,如果找到了end,说明end的parent已经存到了map里,因此可以退出循环,而不用管是否队空
                    flag = true;
                    break;
                }else{
                    //将能够进行一次转换就到字典里的string加到队列,也就是离tmp最近的
                    sb = new StringBuilder(tmp);//重复修改字符串并且是单线程使用StringBuilder
                    for(int i = 0 ; i < tmp.length(); i++){
                        char c = sb.charAt(i);
                        //parentSet = parent.get(tmp);
                        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())){       
		     			levelElements.offer(sb.toString());			
                                        if(parent.containsKey(sb.toString())){
                                            parentSet = parent.get(sb.toString());
                                            parentSet.add(tmp);
                                        }else{
                                            //queue.offer(sb.toString());                   
                                            parentSet = new HashSet<String>();
                                            parentSet.add(tmp);
                                            parent.put(sb.toString(),parentSet);   
                                        }
                                }
                            }
                        }
                        sb.setCharAt(i,c);
                    }
                }
                count++;
            }
	    if(flag){
                break;
            }
	    level = 0;
	    //System.out.println(levelElements); 
	    while(!levelElements.isEmpty()){
	    	String tmp = levelElements.poll();
		if(!visited.contains(tmp)){
			queue.offer(tmp);
			level++;
			visited.add(tmp);
		}
	    }
	    //System.out.println(queue); 
	    //break;
        }
        if(!parent.containsKey(end)){
            return list;
        }
	//System.out.println(parent);
        List<String> tmp = new LinkedList<String>();//用来构造路径
        buildPath(list,tmp,parent,start,end);
        return list;
    }
    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;  
    }
    public void buildPath(List<List<String>> list,List<String> tmp,Map<String,Set<String>> parent,String start,String s){
        if(s.equals(start)){
		tmp.add(0,s);
                List<String> l = new LinkedList<String>();
                for(int i = 0; i < tmp.size(); i ++){//深度复制
                    l.add(tmp.get(i));
                }
		//l.add(0,start);
                list.add(l);
		tmp.remove(s);
	}
	Set<String> set = parent.get(s);//获得s的父亲
        for(String s1: set){
                tmp.add(0,s);
                buildPath(list,tmp,parent,start,s1);
                tmp.remove(s);
        }
    }
}


一共是51组




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: PLC(可编程逻辑控制器)是一种常见的自动控制系统。PLC使用一种特殊的编程语言,被称为类似于C语言的可视化编程语言,这种编程语言通常称为PLC Word。 PLC Word是一种高级编程语言,它具有结构化编程的特点,与C语言类似。它由一系列指令和函数构成,可以用于编写复杂的逻辑控制程序,用于控制各种工业设备和自动化过程。 PLC Word中的指令和函数包括条件语句、循环语句、输入输出控制语句等,可以用于实现各种自动化控制功能。通过使用PLC Word,工程师可以根据具体需求编写逻辑控制程序,并将其加载到PLC中,使其实现所需的控制功能。 与C语言相比,PLC Word更加注重实时性和可靠性,适用于需要高效稳定控制的工业自动化系统。此外,PLC Word还提供了丰富的调试功能,可以帮助工程师快速诊断和解决问题。 总之,PLC Word是一种类似于C语言的可视化编程语言,用于编写PLC的逻辑控制程序。通过PLC Word,工程师可以实现各种自动化控制功能,为工业自动化系统提供高效稳定的控制。 ### 回答2: PLC(可编程逻辑控制器)与C(编程语言)有着相应的对应关系。 PLC是一种特殊的计算机,用于实现工业自动化的控制。它的特点是功能强大、可靠性高,并且适用于工业环境中的各种应用。PLC主要通过控制输入输出信号来完成对设备、机器或生产线的控制。 与之对应的编程语言是C语言。C语言是一种通用的高级编程语言,常用于嵌入式系统的开发。通过C语言,开发人员可以编写代码控制PLC的操作。C语言具备灵活性和高效性,可以实现各种复杂的控制任务。 PLC和C语言的对应关系主要体现在PLC编程语言中的指令和C语言中的代码。PLC编程语言包括与C语言相似的结构,如顺序结构、选择结构和循环结构。PLC编程还可使用Ladder Diagram(梯形图)、Structured Text(结构化文本)等表示方法,与C语言中的代码类似。 同时,PLC编程中可以使用C语言库函数,通过调用这些函数来实现更复杂的功能。例如,可以使用C语言库函数来处理和操作数据、进行数学计算、进行字符串处理等。这使得PLC编程更加便捷和灵活。 总之,PLC和C语言之间的对应关系体现在PLC编程语言的指令和C语言代码的相似之处,以及PLC编程中可以使用C语言库函数的特点。这种对应关系使得开发人员能够更好地利用C语言的特性和功能,实现高效的PLC编程。 ### 回答3: PLC是Programmable Logic Controller的缩写,即可编程逻辑控制器。它是一种专门用于自动化控制的电子设备。而"C"这个词,在这里可以有两种可能的含义。 首先,"C"可以指代C语言。C语言是一种通用的高级程序设计语言,广泛应用于各种领域,包括嵌入式系统的开发。在PLC中,C语言可以用于编写用于控制和监测系统行为的程序。C语言提供了丰富的语法和库函数,使得开发者可以编写出高效、可靠的控制逻辑。 其次,"C"还可以指代操作码。在PLC的指令集中,操作码用于指示PLC执行不同的功能。操作码是PLC Word的一部分,用于定义要执行的特定操作,如读取输入、设置输出、执行算术运算等。PLC根据操作码的不同来执行相应的操作,从而实现系统的自动化控制功能。 总而言之,PLC Word和C之间的对应关系可以是指PLC中使用C语言进行程序设计,也可以是指PLC Word中的操作码与C语言中的指令的对应关系。无论是哪种情况,都可以帮助开发者实现系统的控制和监测功能,并提高自动化系统的性能和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值