126. Word Ladder II
2019年8月29日LeetCode是用C# release模式运行的。
解法1:
使用队列进行广度优先搜索,统一在Dequeue后进行终止条件判断。
缺点是生成下一个状态,enqueue前,没有检查是不是解。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace LeetCode
{
public class WordLadder2
{
public IList<IList<string>> FindLadders(string beginWord, string endWord, IList<string> wordList)
{
var enhancedList = wordList.Select(w => new Problem(w, GetDifferences(w, endWord))).ToList();
var words = new List<Problem>(enhancedList);
var transforms = FindLadders(new Problem(beginWord, GetDifferences(beginWord, endWord)), endWord,
words);
//如果找不到解,按规定返回空列表
if (transforms.Count == 0)
return new List<IList<string>>();
else
{
var result = (from t in transforms
select (IList<string>)t.ToList()).ToList();
Debug.Assert(result.All(t => t[t.Count - 1] == endWord), "最后一个元素必须是endWord。");
Debug.Assert(result.All(t => t[0] == beginWord), "第一个元素必须是beginWord。");
return result;
}
}
/// <summary>
/// 如果beginWord等于endWord,变换长度为0。
/// 如果beginWord无法变换到endWord,返回null。
/// </summary>
/// <param name="beginWord"></param>
/// <param name="endWord"></param>
/// <param name="wordList"></param>
/// <returns></returns>
List<LinkedList<string>> FindLadders(Problem beginWord, string endWord, IList<Problem> wordList)
{
//使用队列进行广度优先搜索
Queue<Problem> queue = new Queue<Problem>();
queue.Enqueue(beginWord);
List<LinkedList<string>> shortestTransformations = new List<LinkedList<string>>();
var wordsUsedInThisLevel = new List<Problem>();
int currentLevel = 0; //根节点深度为0。
while (queue.Count > 0)
{
var problem = queue.Dequeue();
if (problem.UsedTransform > currentLevel) //当前层结束了,已经进入下一层
{
//上一层用过的词,这一次不能再用了,因为上一层某一个分支,用较少的步骤变换到了这个词,这里没必要多一个步骤变换到相同的词
foreach (var w in wordsUsedInThisLevel)
wordList.Remove(w);
wordsUsedInThisLevel.Clear();
currentLevel++;
Debug.Assert(currentLevel == problem.UsedTransform);
}
if (problem.Word == endWord)
{
var transform = ChainTransform(problem);
shortestTransformations.Add(transform);
}
else
{
var nextWords = from w in wordList
where IsDifference1(w.Word, problem.Word)
select w;
foreach (var w in nextWords)
{
var p = new Problem(w.Word, w.DifferenceFromSolution, problem);
wordsUsedInThisLevel.Add(p);
queue.Enqueue(p);
}
}
}
return shortestTransformations;
}
bool IsDifference1(string w1, string w2)
{
Debug.Assert(w1.Length == w2.Length);
bool hasDifference = false;
for (int i = 0; i < w1.Length; i++)
{
if (w1[i] != w2[i])
{
if (hasDifference)
return false;
else
hasDifference = true;
}
}
return hasDifference;
}
int GetDifferences(string w1, string w2)
{
Debug.Assert(w1.Length == w2.Length);
//对特殊情况进行优化
if (w1 == w2)
return 0;
int hasDifference = 0;
for (int i = 0; i < w1.Length; i++)
{
if (w1[i] != w2[i])
hasDifference++;
}
return hasDifference;
}
LinkedList<string> ChainTransform(Problem p)
{
var list = new LinkedList<string>();
while (p != null)
{
list.AddFirst(p.Word);
p = p.Parent;
}
return list;
}
[DebuggerDisplay("Word={Word}, d={DifferenceFromSolution}")]
class Problem: IEquatable<Problem>
{
public Problem(string word, int differenceFromSolution, Problem parent = null)
{
Word = word;
DifferenceFromSolution = differenceFromSolution;
Parent = parent;
if (parent != null)
UsedTransform = parent.UsedTransform + 1;
}
public string Word { get; }
public int DifferenceFromSolution { get; }
/// <summary>
/// 从初始状态到达此状态已经使用的变换数量
/// </summary>
public int UsedTransform { get; }
public int Heuristic => DifferenceFromSolution + UsedTransform;
public Problem Parent { get; }
public bool Equals(Problem other)
{
if (other == null)
return false;
return Word == other.Word;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Problem) obj);
}
public override int GetHashCode()
{
return (Word != null ? Word.GetHashCode() : 0);
}
}
}
}
不需要记录solutionLevel。
List> FindLadders(Problem beginWord, string endWord, IList wordList)
{
//使用队列进行广度优先搜索
Queue queue = new Queue();
queue.Enqueue(beginWord);
List> shortestTransformations = new List>();
var wordsUsedInThisLevel = new List();
int currentLevel = 0; //根节点深度为0。
//int solutionLevel = int.MaxValue;
while (queue.Count > 0)
{
var problem = queue.Dequeue();
//不需要此优化。如果solutionLevel有值,表示已经搜索过0到solutionLevel,才发现解。一旦在该层发现解,程序不会搜索solutionLevel+1层。
//if (problem.UsedTransform > solutionLevel)
// continue;
if (problem.UsedTransform > currentLevel) //当前层结束了,已经进入下一层
{
//上一层用过的词,这一次不能再用了,因为上一层某一个分支,用较少的步骤变换到了这个词,这里没必要多一个步骤变换到相同的词
foreach (var w in wordsUsedInThisLevel)
wordList.Remove(w);
wordsUsedInThisLevel.Clear();
currentLevel++;
Debug.Assert(currentLevel == problem.UsedTransform);
}
if (problem.Word == endWord)
{
var transform = ChainTransform(problem);
shortestTransformations.Add(transform);
//solutionLevel = problem.UsedTransform;
}
else
{
var nextWords = from w in wordList
where IsDifference1(w.Word, problem.Word)
select w;
foreach (var w in nextWords)
{
var p = new Problem(w.Word, w.DifferenceFromSolution, problem);
wordsUsedInThisLevel.Add(p);
queue.Enqueue(p);
}
}
}
return shortestTransformations;
}