首页 > 试题广场 >

词语序列

[编程题]词语序列
  • 热度指数:20433 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
给定两个单词(初始单词和目标单词)和一个单词字典,请找出所有的从初始单词到目标单词的最短转换序列的长度:
  1. 每一次转换只能改变一个单词
  2. 每一个中间词都必须存在单词字典当中
例如:
给定的初始单词start="red",
目标单词end ="tax"。
单词字典dict =["ted","tex","red","tax","tad","den","rex","pee"]
一个最短的转换序列为"red" -> "ted" -> "tad" -> "tax",
返回长度4

注意:
如果没有符合条件的转换序列,返回0。
题目中给出的所有单词的长度都是相同的
题目中给出的所有单词都仅包含小写字母

//主要思想:广度优先搜索。先构造一个字符串队列,并将start加入队列。1.对队列头字符串做单个字符替换
//每次替换后,2.判断是否和end匹配,如果匹配,返回答案;3.没有匹配,则在字典里面查询是否有“邻居字符串”,
//如果有,则将该字符串加入队列,同时将该字符串从字典里删除。重复1的过程,知道和end匹配。如果最后队列
//为空还未匹配到,则返回0.
   int ladderLength(string start, string end, unordered_set<string>&dict)
{
	queue<string>Q;
	Q.push(start);
	int res = 1;
	while (!Q.empty())
	{
		int qsize = Q.size(); //层次遍历,记录当前队列大小
		while (qsize--)
		{
			string cur_front = Q.front();
			Q.pop();
			int size = cur_front.size();
			for (int i = 0; i < size; i++)
			{
				char ch = cur_front[i];  //对队列头字符串每一个字符进行替换
				for (int j = 0; j < 26; j++)
				{
					cur_front[i] = 'a' + j;//替换字符
					if (cur_front == end)return res+1;//找到答案,退出
					if (dict.find(cur_front) != dict.end())
					{
						Q.push(cur_front);//变换在字典中找到了
						dict.erase(cur_front);//从字典中删除
					}
				}
				cur_front[i] = ch;//还原队列头字符串,因为有可能在字典中可以找到多个“近邻字符串”
			}
		}
		res++;//遍历一层,res+1
	}
       return 0;
}

发表于 2017-03-24 16:31:48 回复(5)
//使用bfs(广度优先搜素),其中res用于记录结果,size用于记录每一层的元素个数,遍历完一层res++
//注意遍历过的要从字典中删除。

public int ladderLength(String start, String end, HashSet<String> dict) {  
      int res=1;
      LinkedList<String> queue=new LinkedList<>();
      queue.offer(start);
      while(!queue.isEmpty())
      {
    	  int size=queue.size();
    	  while(size>0)
    	  {
    		  String s=queue.poll();
    		  size--;
    		  
    		  if(isDiffOne(s, end))
    			  return res+1;
    		  for(Iterator<String> it=dict.iterator();it.hasNext();)
    		  {
    			  String str=it.next();
    			  if(isDiffOne(str, s))
    			  {
    				  queue.offer(str);
    				  it.remove();
    			  }
    				  
    		  }
    	  }
    	  res++;
      }
      return 0;
    }  
  
    public boolean isDiffOne(String w1, String w2) {  
       int count = 0;          
       for(int i = 0; i < w1.length(); i++) {  
           if(w1.charAt(i) != w2.charAt(i)) {  
               count++;  
           }  
       }
       return count==1?true:false;
    }

发表于 2017-04-18 22:35:09 回复(3)

思路:
1.先把字典里面的start删掉,然后插入end。
2.每一个圆圈代表当前队列里面的单词。队列一开始只有start
3.访问过的单词不再访问
4.当队列里面包含end单词时停止循环,此长度就是最短长度。
代码:
class Solution {
public:
    int  ladderLength(string start,string end,unordered_set<string> &dict){
         dict.insert(end);
         dict.erase(start);
         queue<string> q;
         q.push(start);
         int length=0;
         while(q.size()>0)
         {    
             length++;
             int QueueLength=q.size();
             for(int i=0;i<QueueLength;i++)
                {  
                  start=q.front();
                  q.pop();
                  if(start==end) return length;
                  vector<string> deleteList;
                  for(unordered_set<string >::iterator iter=dict.begin();iter!=dict.end();iter++)
                   {  
                     int dis=distance(start,*iter);
                     if(dis==1)
                       {
                        q.push(*iter);
                        deleteList.push_back(*iter);
                       }
                    }
                  for(int i=0;i<deleteList.size();i++)
                        dict.erase(deleteList[i]);
                  }
          }
        return 0;
    }
    int distance(string s1,string s2)
    {
        int count=0;
        for(int i=0;i<s1.size();i++)
        {
            if(s1[i]!=s2[i]) count++;
            
        }
        return count;
        
        
    }
};


编辑于 2018-03-02 00:37:59 回复(2)
class Solution {
public:
    int ladderLength(string start, string end, unordered_set<string> &dict) {
      int res=1;
      queue<string> que;
      unordered_set<string>::iterator it;
      que.push(start);
      while(!que.empty())
      {
          int size=que.size();
          while(size--)
          {
              string s=que.front();
              que.pop();
               
              if(IsSuit(s, end))
                  return res+1;
              for(it=dict.begin();it!=dict.end();)
              {
                  string str=*it;
                  if(IsSuit(s,str))
                  {
                      que.push(str);
                      it=dict.erase(it);
                  }
                  else
                      ++it;
              }
          }
          res++;
      }
      return 0;
    } 
    
    bool IsSuit(string &s,string &str)
    {
        int num=s.length();
        int a=0;
        for(int i=0;i<num;++i)
        {
            if(s[i]!=str[i])
                ++a;
            if(a>1)
                return false;
        }
        if(a==1)
            return true;
        return false;
    }
};

发表于 2017-07-30 23:35:56 回复(2)
使用广度优先遍历种。
0)将start放入队列q1,初始count=0。
1)当q1非空,逐个遍历q1中的单词,count++;
2)设q1中的单词为str,寻找其邻近单词s,如果s等于end返回count+1;
否则将s放入队列q2中;
3)将q1与q2交换,继续执行1)。
其中代码修改了dict。

    int ladderLength(string start, string end, unordered_set<string> &dict) {
        queue<string> q1,q2,*p1,*p2;
        int count=0;
        for(p1=&q1,p2=&q2,p1->push(start);p1->size();swap(p1,p2)){
            ++count;
            for(string str;p1->size();p1->pop()){
                str = p1->front();           
            	for(int i=0;i<str.length();++i){
                	string s=str;
                    for(char ch='a';ch<='z';++ch){
                        if (ch==str[i]) continue;
                        s[i] = ch;
                        if (s==end) return count+1;
                        if (dict.find(s)!=dict.end()){
                            p2->push(s);
                            dict.erase(s);    // 防止死循环(回路)
                        }
                    }
                }
            }
        }
        return 0;
    }

编辑于 2016-08-05 14:18:50 回复(1)
class Solution {
public:
  int ladderLength(string start, string end, unordered_set<string> &dict) {
    if (!dict.count(end)) return 0;

    queue<string> q{{start}};
    unordered_set<string> seen{start};

    int steps = 0;
    while (not q.empty()) {
      size_t s = q.size();
      while (s--) {
        const auto curr = q.front(); q.pop();
        if (curr == end) return steps + 1;
        for (int i = 0; i < curr.length(); ++i) {
          string nxt(curr);
          const char c = curr[i];
          for (char j = 'a'; j <= 'z'; ++j) {
            if (j == c) continue;
            nxt[i] = j; 
            if (seen.count(nxt) || !dict.count(nxt)) continue;
            q.emplace(nxt);
            seen.emplace(nxt);
          }
        }
      }
      ++steps;
    }

    return 0;
  }
};

发表于 2021-09-25 19:16:16 回复(0)
双向BFS
import java.util.*;

class Solution {
    /**
     *  双向BFS,减少容器的使用的情况下减少比较次数
     *  1. 创建两个初始集合,一个保存从前向后遍历的候选词
     *     另一个保存从后向前的候选词
     *  2. 两个集合取数量小的一个进行遍历,判断词的下一个词
     *     是否就在另一个集合中,如果在的话,返回当前计数+1
     *  3. 如果下一个词不在另一个集合,但是在总集合中,从总
     *     集合擦除该词,遍历后得到新的候选词集合,替换掉当前
     *     被遍历的集合后,计数+1
     *  4. 其他退出条件:如果某一个集合为空,代表没有找到路径
     *     返回0
     */   
    public int ladderLength(String beginWord, String endWord, HashSet<String> dict) {
        int max = dict.size()+1;
        Set<String> aSet = new HashSet<>(max); 
        Set<String> bSet = new HashSet<>(max);
        int count = 1;
        if(!dict.contains(endWord)) return 0;
        aSet.add(beginWord);
        bSet.add(endWord);
        dict.remove(beginWord);
        dict.remove(endWord);
        while(!aSet.isEmpty()&&!bSet.isEmpty()){
            if(aSet.size()>bSet.size()){
                Set<String> tmp = aSet;
                aSet = bSet;
                bSet = tmp;
            }
            Set<String> next = new HashSet<>(max);
            for(String word:aSet){
                char[] chars = word.toCharArray();
                for(int i=0; i<chars.length; i++){
                    for(char c='a'; c<='z'; c++){
                        if(c==chars[i]) continue;
                        char tmp = chars[i];
                        chars[i] = c;
                        String trans = new String(chars);
                        chars[i] = tmp;
                        if(bSet.contains(trans)){
                            return count+1;
                        }
                        if(dict.contains(trans)){
                            dict.remove(trans);
                            next.add(trans);
                        }
                    }
                }
            }
            aSet = next;
            count++;
        }
        return 0;
    }
}


分享最初使用的笨方法,写起来比较罗嗦,时间复杂度还阔以,但是辅助容器用的比较多,胜在好理解,虽然比最优解要慢一倍,但是有这个思考过程,对上面解法理解比较快
import java.util.*;

class Solution {
    class Graph{
        String word;
        List<Graph> nexts;
        public Graph(String word, List<Graph> nexts){
            this.word = word;
            this.nexts = nexts;
        }
    }
    /*
     *  1. 首先判断下endWord是否在wordList中,不在的话根据题意直接返回0
     *  2. 创建邻接表将wordList以及beginWord放入图中
     *      邻接链表的创建原则是当两个词只差一个字母(题目已经限定所有词等长)
     *      问题转化为判断两个词只差一个单词
     *      可以遍历wordList,逐一将每一个字母用"*"替换,然后保存到Map中
     *      这个Map用替换掉某一位字母的字符串为key, 将所有只有某一位不同的单次保存在同一个List中
     *      建立过程记得通过第一步判断的数组索引保存好目标节点的引用,用于下一步最短路径的判断
     *  3. 建立好图以后,知道起始节点和目标节点,可以通过BFS求最短路径
     *      建一个新的Set保存已经遍历过的节点,因为是BFS,所以已经遍历过的节点,到目标节点的距离
     *      肯定比当前节点要短(当前图是无权无向图),求最短路径不需要再去判断
     *      如果不存在路径,返回0
     *  遇到的几个坑:
     *      1) 起始词不能作为接龙词,不代表起始词不在词典中,要处理这种不在的情况
     *      2) 构建邻接关系时候,因为满足条件的结果中肯定包含自己(因为是用*替换的,包含原本字符的情况),这个要排除
     */
    public int ladderLength(String beginWord, String endWord, HashSet<String> dict) {
        
        int n = dict.size();
        if(!dict.contains(endWord)) return 0;
        
        // 方便遍历操作,初始元素如果不在词典中要放入词典(题目是说初始元素不能作为接龙元素)
        dict.add(beginWord);
        Map<String, Graph> created = new HashMap<>(n+1);
        Map<String, List<Graph>> match = new HashMap<>(n+1);
        
        Graph start = new Graph(beginWord, new ArrayList<>());
        created.put(beginWord, start);
        Graph end = new Graph(endWord, new ArrayList<>());
        created.put(endWord, end);
        
        // 创建图的顶点以及保存按照每一位字符擦出的特征信息O(nm)
        for(String word:dict){
            if(!created.containsKey(word)){
                created.put(word, new Graph(word, new ArrayList<>()));
            }
            Graph vertex = created.get(word);
            char[] chars = new char[word.length()];
            word.getChars(0, word.length(), chars, 0);
            for(int i=0; i < chars.length; i++){
                char tmp = chars[i];
                chars[i] = '*';
                String m = new String(chars);
                if(!match.containsKey(m)){
                    match.put(m, new ArrayList<>());
                }
                match.get(m).add(vertex);
                chars[i] = tmp;
            }
        }   
        // 保存邻接信息O(n^2),将之前遍历得到的所有只差一个字符的非本身元素保存到nexts中去
        for(String word:dict){
            Graph vertex = created.get(word);
            char[] chars = new char[word.length()];
            word.getChars(0, word.length(), chars, 0);
            for(int i=0; i < chars.length; i++){
                char tmp = chars[i];
                chars[i] = '*';
                List<Graph> mlist = match.get(new String(chars));
                chars[i] = tmp;
                // 接龙元素不能重复使用,不能自己指向自己
                for(Graph next:mlist){
                    if(next!=vertex){
                        vertex.nexts.add(next);
                    }
                }
            }
        }
        // 通过BFS找到从start到end的最短路径
        int min = 0;
        Set<Graph> searched = new HashSet<>(n+1);
        List<Graph> path = new ArrayList<>(n+1);
        Queue<List<Graph>> pathes = new LinkedList<>();
        path.add(start);
        pathes.add(path);
        searched.add(start);
        // 队列pathes中保存的需要继续遍历(还有可以后续顶点可以遍历)的待遍历路径
        while(!pathes.isEmpty()){
            List<Graph> curPath = pathes.remove();
            Graph last = curPath.get(curPath.size()-1);
            if(last == end){
                // min=0时代表无解
                min = min==0 ? curPath.size() : Math.min(min, curPath.size());
                continue;
            }
            for(Graph next:last.nexts){
                // 没有后续的未遍历顶点跳过
                if(!searched.contains(next)){
                    List<Graph> nextPath = new ArrayList<>(curPath);
                    nextPath.add(next);
                    pathes.add(nextPath);
                    searched.add(next);
                }
            }
        }
        return min;        
    }
}


编辑于 2020-01-06 17:11:31 回复(0)
import java.util.*;
public class Solution {
    public int ladderLength(String start, String end, HashSet<String> dict) {
        int wordSize = start.length(), level = 1;
        Queue<ArrayList<String>> paths = new LinkedList<ArrayList<String>>();
        paths.offer(new ArrayList<String>() {{
            add(start);
        }});
        ArrayList<String> words = new ArrayList<String>();
        words.add(start);
        while(!paths.isEmpty()){
            ArrayList<String> path = paths.poll();
            int currentSize = path.size();
            if(currentSize > level){
                for(String str : words){
                    dict.remove(str);
                }
                words.clear();
                level = currentSize;
            }

            String lastWord = path.get(currentSize - 1);
            for(int i = 0; i < wordSize; i++){
                for(char c = 'z'; c >= 'a'; c--){
                    char[] charArr = lastWord.toCharArray();
                    charArr[i] = c;
                    String newWord = new String(charArr);
                    if(newWord.equals(end)){
                        return currentSize + 1;
                    }else if(dict.contains(newWord)){
                        words.add(newWord);
                        paths.offer(deepCopy(path, newWord));
                    }
                }
            }
        }
        return 0;
    }
    static ArrayList<String> deepCopy(ArrayList<String> arr, String newWord){
        ArrayList<String> newPath = new ArrayList<String>();
        for(String str : arr){
            newPath.add(str);
        }
        newPath.add(newWord);
        return newPath;
    }
}

发表于 2019-10-13 16:12:15 回复(0)
BFS,用last和nlast变量标记是否换层
int ladderLength(string start, string end, unordered_set<string> &dict) {
        deque<string> que;
        que.push_back(start);
        int level=1;
        string last=start;
        string nlast="";
        while(!que.empty()){
            string s=que.front();
            que.pop_front();
            //逐个变s
            for(int i=0;i<s.length();i++){
                char tmp=s[i];
                for(int j=0;j<26;j++){
                    s[i]='a'+j;
                    //变换得到end
                    if(s==end){
                        return level+1;
                    }
                    if(dict.find(s)!=dict.end()){
                        que.push_back(s);
                        dict.erase(s);
                        nlast=s;
                    }
                }
                s[i]=tmp;
            }
            if(s==last){
                level++;
                last=nlast;
            }
        }
        return 0;
    }

发表于 2017-11-07 15:30:58 回复(0)
class Solution {
public:
    int ladderLength(string start, string end, unordered_set<string> &dict) {
        queue<string> q1,q2,*p1,*p2;
        int count = 0;
        p1 = &q1;         p2 = &q2;          for(p1->push(start);!p1->empty();swap(p1,p2))         {             count++;             for(;!p1->empty();p1->pop())             {                 string s = p1->front();                 for(int i=0;i<s.length();i++)                 {                     string t = s;                     for(char c='a';c<='z';c++)                     {                         if(c==s[i])                             continue;                         t[i] = c;                         if(t==end)                             return count+1;                         if(dict.find(t) != dict.end())                         {                             p2->push(t);                             dict.erase(t);                         }                     }                 }             }         }
        return 0;
    }
};

发表于 2017-09-30 01:51:39 回复(0)
//有没有大佬解释一下为什么我用注释里的构造next的方***报超时,
//改成转数组的方式就直接AC了?先谢过了
import java.util.*;

public class Solution {
    public int ladderLength(String start, String end, HashSet<String> dict) {
		
        Queue<String> que = new LinkedList<String> ();
        HashSet<String> visited = new HashSet<String> ();
        Queue<Integer> num = new LinkedList<Integer> ();

        que.add(start);
        num.add(1);
        visited.add(start);
        
        while(!que.isEmpty()){
            String str = que.remove();
            int nums = num.remove();
            if(str.equals(end))
                return nums;
            for(int i = 0; i < start.length(); ++i){
                char [] arr = str.toCharArray();
                for(char c = 'a'; c <= 'z'; ++c){
                    arr[i] = c;
                    String next = new String(arr);
                    //String next = str.substring(0,i) + c + str.substring(i+1,start.length());
                    if(dict.contains(next) && !visited.contains(next)){
                        que.add(next);
                        num.add(nums+1);
                        visited.add(next);
                    }
                }
            }
        }
        
        return 0;
    }
}

发表于 2017-09-11 19:24:39 回复(0)
package go.jacob.day0530.queue;

import java.util.*;

/**
 * 解法:
 * 该题一样可以简化为图论的问题
 */
public class P127_WordLadder {

    public static int ladderLength(String beginWord, String endWord, HashSet<String> dict) {
        Queue<Pair> queue = new LinkedList<Pair>();
        queue.offer(new Pair(beginWord, 1));
        Set<String> visited = new HashSet<String>();

        if (!dict.contains(endWord))
            return 0;

        while (!queue.isEmpty()) {
            String str = queue.peek().str;
            int step = queue.peek().step;
            queue.poll();

            for (int i = 0; i < str.length(); i++) {
                char[] chars = str.toCharArray();
                for (char c = 'a'; c <= 'z'; c++) {
                    chars[i] = c;

                    String word = new String(chars);
                    if (word.equals(endWord))
                        return step + 1;

                    if (dict.contains(word) && !visited.contains(word)) {
                        queue.add(new Pair(word, step + 1));
                        visited.add(word);
                    }
                }
            }
        }

        return 0;
    }

    static class Pair {
        public String str;
        public int step;

        public Pair(String str, int step) {
            this.str = str;
            this.step = step;
        }
    }


}









编辑于 2018-05-30 18:35:49 回复(0)
import java.util.*;

public class Solution
{
    public int ladderLength(String start, String end, HashSet<String> dict)
    {
        if(start == null || end == null || start.length() == 0 || end.length() == 0)
            return 0 ;

        dict.remove(start) ;
        dict.remove(end) ;

        if(isDifferOne(start , end))
            return 2 ;

        int l = bfs(start , end , dict) ;
        return l < 0 ? 0 : l+1 ;
    }

    private int bfs(String start , String end , HashSet<String> dict)
    {
        Iterator<String> it = dict.iterator() ;
        Queue<String> queue = new LinkedList<>() ;

        while (it.hasNext())
        {
            String str = it.next() ;
            if(isDifferOne(start , str))
            {
                queue.add(str) ;
                it.remove() ;
            }
        }

        int l = bfs(queue , end , dict) ;
        return l < 0 ? -1 : l+1 ;
    }

    private int bfs(Queue<String> queue , String end , HashSet<String> dict)
    {
        int size = queue.size() ;

        for(int i=0 ; i<size ; i++)
        {
            Iterator<String> it = dict.iterator() ;
            String str = queue.poll() ;
            if(isDifferOne(str , end))
                return 1 ;

            while (it.hasNext())
            {
                String next = it.next() ;
                if(isDifferOne(str , next))
                {
                    queue.add(next) ;
                    it.remove() ;
                }
            }
        }

        if(queue.size() == 0)
            return -1 ;

        int l = bfs(queue , end , dict) ;

        return l<0 ? -1 : l+1 ;
    }

    private boolean isDifferOne(String s1 , String s2)
    {
        int differ = 0 ;
        for(int i=0 ; i<s1.length() ; i++)
        {
            if(s1.charAt(i) != s2.charAt(i))
                differ++ ;
            if(differ > 1)
                return false ;
        }

        return true ;
    }
}
使用bfs

发表于 2016-08-09 14:38:49 回复(0)

解题思路 :

将两个串之间的最短距离问题转化为图中节点的最短距离,运用迪杰斯特拉算法进行BFS .

但是用两层嵌套来建立图的话复杂度为 n^2, 会超时,所以本题没有建立图,而是在BFS过程中来判断某个串可达的下一个串是否在Set中 . 判断方法为 : 用26个字母依次替换串中的每一个字符,即穷举出所有满足条件的可达串,这样复杂度为26*length,当length很大时,会远小于 length*length.
publicintladderLength(String start, String end, HashSet<String> dict) {
        Iterator<String> iterator = dict.iterator();
        while(iterator.hasNext()) {
            String next = iterator.next();
            if(next.equals(start))
                iterator.remove();
        }
 
        //存放可达但未处理的串的队列
        Queue<String> unProcess = newLinkedList<>();
        //初始化start到各个串的最短距离,-1表示都不可达
        Map<String, Integer> lengthMap = newHashMap();
        iterator = dict.iterator();
        while(iterator.hasNext()) {
            String next = iterator.next();
            if(isTarger(start, next)) {
                lengthMap.put(next, 2);
                unProcess.add(next);
            } else
                lengthMap.put(next, -1);
        }
 
        //迪杰斯特拉算法来寻找最短路径
        while(!unProcess.isEmpty()) {
            String processing = unProcess.poll();
            for(inti = 0; i < processing.length(); i++) {
                for(charc = 'a'; c <= 'z'; c++) {
                    if(processing.charAt(i) != c) {
                        String newProcessing = processing.substring(0, i) + c + processing.substring(i + 1, processing.length());
                        if(dict.contains(newProcessing) && lengthMap.get(newProcessing) == -1) {
                            unProcess.add(newProcessing);
                            lengthMap.put(newProcessing, lengthMap.get(processing) + 1);
                        }
                    }
                }
            }
        }
 
        intshoest = Integer.MAX_VALUE;
        //遍历各个串,判断是否能达终点
        iterator = lengthMap.keySet().iterator();
        while(iterator.hasNext()) {
            String next = iterator.next();
            if(end.equals(next))
                if(lengthMap.get(next)
                        < shoest)
                    shoest = lengthMap.get(next);
            if(isTarger(next, end))
                if(lengthMap.get(next) + 1< shoest)
                    shoest = lengthMap.get(next) + 1;
        }
        if(shoest == Integer.MAX_VALUE || shoest == -1)
            return0;
        else
            returnshoest;
    }
 
    publicstaticbooleanisTarger(String s1, String s2) {
        intcount = 0;
        for(inti = 0; i < s1.length(); i++) {
            if(s1.charAt(i) != s2.charAt(i))
                count++;
            if(count > 1)
                returnfalse;
        }
        returntrue;
    }

发表于 2016-11-15 10:09:35 回复(0)
这题的本质就是算树的深度啊,用层序遍历的思想做。
要注意的是,queue中的节点不能出现在set中,目的是为了防止反向遍历。


import java.util.*;
public class Solution {
    public int ladderLength(String beginWord, String endWord, HashSet<String> set) {
        int depth = 1;
        Queue<String> queue = new LinkedList(); 
        queue.offer(beginWord);
        int cnt = queue.size();
        boolean flag = false;
        while(!queue.isEmpty()){
            for(String s:queue)
                set.remove(s);
            cnt--;
            String a = queue.poll();
            if(a.equals(endWord)){
                flag = true;
                break;
            }
            for(String str:set){
                 if(distance(a,str)){
                     queue.offer(str);
                 }
            }
            if(cnt==0){
                cnt = queue.size();
                depth++;
            }
        }
        if(flag)
            return depth;
        else
            return 0;
    }
    private boolean distance(String a,String b){
        int cnt = 0;
        if(a.length()!=b.length())
            return false;
        for(int i=0;i<a.length();i++){
            if(a.charAt(i)!=b.charAt(i))
                cnt++;
        }
        return cnt==1?true:false;
    }
}

发表于 2019-04-19 11:37:04 回复(0)

没做过这种图论的题目,一脸懵逼😳,看了大佬的解析,自己再总结(fushu)一遍:

将所有路径表示出来——

        ->lot->log
hit->hot          ->cog
        ->dot->dog

这个其实就是求图论中的单源最短路径,图中边是没有权重的,用BFS求解最合适(时间复杂度O(n))。

class Solution {
public:
    int ladderLength(string start, string end, unordered_set &dict) {
        queue> record;
        record.push(make_pair(start, 1));
        while (!record.empty()) {
            pair current = record.front();
            record.pop();
            if (current.first == end) {
                return current.second;
            }
            // 替换每一个字符,并在set中搜寻
            for (int i = 0; i < current.first.size(); ++i) {
                for (int j = 0; j < 26; ++j) {
                    string str = current.first;
                    str[i] = 'a' + j;
                    if (dict.find(str) != dict.end()) {
                        record.push(make_pair(str, current.second+1));
                        dict.erase(str);
                    }
                }
            }
        }
        return 0;
    }
};
编辑于 2020-08-09 15:33:45 回复(0)
class Solution{
    /*
	双向bfs并不是从两端同时bfs, 实际上是这次从begin进行bfs, 下次从end进行bfs, 像这样循环操作
	*/
	private static int ladderLength(String beginWord, String endWord, HashSet<String> dict) {
		//endWord也是transformed word, 所以必须存在于wordList中, 否则返回0, 表示无法从beginWord变成endWord
		if(!dict.contains(endWord))
			return 0;
		//每个单词长度相同
		int n = beginWord.length();
		//key是通用状态; value是拥有该通用状态的词
		HashMap<String, ArrayList<String>> allcommons = new HashMap<>();
		//记录wordList中所有元素对应的所有通用状态
        //curword当前正在遍历的单词
		for(String curword:dict) {
			for(int i=0; i<n; i++) {//每个单词都能得到n种通配词,每个位置都可变为*
				String common = curword.substring(0,i)+"*"+curword.substring(i+1);
				if(!allcommons.containsKey(common))
					allcommons.put(common, new ArrayList<>());
				allcommons.get(common).add(curword);
			}
		}
		//使用HashSet实现宽度优先遍历bfs
		HashSet<String> begin = new HashSet<>();
		HashSet<String> end = new HashSet<>();
		begin.add(beginWord);
		end.add(endWord);
		//记录访问过的节点
		HashSet<String> visited = new HashSet<>();
		//返回值的初始化, 由于beginWord!=endWord, 所以至少需要一步变化
		int len = 1;
		while(!begin.isEmpty()&&!end.isEmpty()) {
			//记录遍历begin时每个元素的邻居, 也就是cur的邻居
			HashSet<String> neighbor = new HashSet<>();
			for(String cur:begin) {//遍历cur的邻居
				for(int i=0; i<n; i++) {
					//有了all_commons哈希表,就不用每个位置都遍历'a'~'z'了
                    //细节:如果cur是beginWord的话, all_commons没有统计beginWord的通用状态,
					//所以all_commons.get(tmp)可能返回null, 所以要提前检查一下
					String tmp = cur.substring(0,i)+"*"+cur.substring(i+1);
					if(!allcommons.containsKey(tmp))
						continue;
					for(String str:allcommons.get(tmp)) {
						if(end.contains(str))
							return len+1;
						if(!visited.contains(str)) {
							visited.add(str);
							//记录cur的邻居
							neighbor.add(str);
						}
					}
				}
			}
			//处理完begin中的元素后, 让begin指向begin中的元素的邻居
			begin = neighbor;
			len++;
		}
		//执行到这里说明双向bfs没有相遇, 也就是没有找到从beginWord到endWord的路径
		return 0;
	}
	
}
}

发表于 2020-05-03 16:06:05 回复(0)
广度优先搜索 注意remove已经遍历的
发表于 2020-05-02 10:42:02 回复(0)

有点难理解

发表于 2020-02-14 10:49:49 回复(0)
class Solution {
public:
    int ladderLength(string start, string end, unordered_set<string> &dict) {
        int length = 2;
        //广度遍历,存储每层节点
        std::vector<string> ceil;
        ceil.push_back(start);
        while (true)
        {
            //存储下一层节点
            std::vector<string> nextCeil;
            if (ceil.empty())
            {
                return 0;
            }
            for (auto& sibling : ceil)
            {
                std::unordered_set<string>::iterator iter = dict.begin();
                if (dict.empty())
                {
                    return 0;
                }
                for (; iter != dict.end(); )
                {

                    //是否为当前节点的子节点
                    if (diffCharCount(sibling, *iter) == 1)
                    {
                        //是否以结束
                        if (diffCharCount(end, *iter) == 0)
                        {
                            return length;
                        }
                        nextCeil.push_back(*iter);
                        dict.erase(iter++);
                    }
                    else
                    {
                        ++iter;
                    }
                }
            }
            std::swap(ceil, nextCeil);
            length++;
        }
    }
    
    //判断两个字符差的数量,0 相等,1 相差一个,2最小相差两个
    int diffCharCount(const string& left, const string& right)
    {
        int diffCnt = 0;
        if (left.length() != right.length())
        {
            return -1;
        }
        
        for (int i = 0; i < left.length(); ++i)
        {
            if (left.at(i) != right.at(i))
            {
                ++diffCnt;
                if (diffCnt > 1)
                {
                    return diffCnt;
                }
            }
        }
        return diffCnt;
    }
};
发表于 2020-01-05 16:56:30 回复(0)