我遇到了edit-distance问题的变体:
设计一种将源词转换为目标词的算法。例如:从头到尾,在每一步中,你只需要替换一个字符,并且该字必须有效。你会得到一本字典。
它显然是edit distance问题的变体,但在编辑距离中我不关心这个词是否有效。那么如何添加此要求来编辑距离。
答案 0 :(得分:43)
这可以建模为图形问题。您可以将这些单词视为图形的节点,并且当且仅当它们具有相同的长度并且在一个char中不同时,才连接两个节点。
您可以预处理字典并创建此图表,如下所示:
stack jack
| |
| |
smack back -- pack -- pick
然后你可以有一个从单词到表示单词的节点的映射,为此你可以使用哈希表,高度平衡的BST ......
一旦你有了上面的映射,你所要做的就是看看两个图节点之间是否存在路径,这可以使用BFS或DFS轻松完成。
因此,您可以将算法概括为:
preprocess the dictionary and create the graph.
Given the two inputs words w1 and w2
if length(w1) != length(w2)
Not possible to convert
else
n1 = get_node(w1)
n2 = get_node(w2)
if(path_exists(n1,n2))
Possible and nodes in the path represent intermediary words
else
Not possible
答案 1 :(得分:10)
codaddict的图方法是有效的,尽管构建每个图需要O(n ^ 2)时间,其中n是给定长度的字数。如果这是一个问题,您可以更有效地构建bk-tree,这样就可以找到目标词的给定编辑距离(在本例中为1)的所有单词。
答案 2 :(得分:3)
创建一个图表,每个节点代表字典中的单词。如果两个字节点的编辑距离为1,则在两个字节点之间添加边。然后,所需的最小转换次数将是源节点和目标节点之间的最短路径长度。
答案 3 :(得分:2)
我认为这不是编辑距离。
我认为这可以使用图表完成。只需从字典中构建一个图形,并尝试使用您喜欢的图形遍历算法导航到目的地。
答案 4 :(得分:1)
您可以简单地使用递归反向跟踪,但这远非最佳解决方案。
# Given two words of equal length that are in a dictionary, write a method to transform one word into another word by changing only
# one letter at a time. The new word you get in each step must be in the
# dictionary.
# def transform(english_words, start, end):
# transform(english_words, 'damp', 'like')
# ['damp', 'lamp', 'limp', 'lime', 'like']
# ['damp', 'camp', 'came', 'lame', 'lime', 'like']
def is_diff_one(str1, str2):
if len(str1) != len(str2):
return False
count = 0
for i in range(0, len(str1)):
if str1[i] != str2[i]:
count = count + 1
if count == 1:
return True
return False
potential_ans = []
def transform(english_words, start, end, count):
global potential_ans
if count == 0:
count = count + 1
potential_ans = [start]
if start == end:
print potential_ans
return potential_ans
for w in english_words:
if is_diff_one(w, start) and w not in potential_ans:
potential_ans.append(w)
transform(english_words, w, end, count)
potential_ans[:-1]
return None
english_words = set(['damp', 'camp', 'came', 'lame', 'lime', 'like'])
transform(english_words, 'damp', 'lame', 0)
答案 5 :(得分:0)
这是使用BFS解决问题的C#代码:
//use a hash set for a fast check if a word is already in the dictionary
static HashSet<string> Dictionary = new HashSet<string>();
//dictionary used to find the parent in every node in the graph and to avoid traversing an already traversed node
static Dictionary<string, string> parents = new Dictionary<string, string>();
public static List<string> FindPath(List<string> input, string start, string end)
{
char[] allcharacters = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
foreach (string s in input)
Dictionary.Add(s);
List<string> currentFrontier = new List<string>();
List<string> nextFrontier = new List<string>();
currentFrontier.Add(start);
while (currentFrontier.Count > 0)
{
foreach (string s in currentFrontier)
{
for (int i = 0; i < s.Length; i++)
{
foreach (char c in allcharacters)
{
StringBuilder newWordBuilder = new StringBuilder(s);
newWordBuilder[i] = c;
string newWord = newWordBuilder.ToString();
if (Dictionary.Contains(newWord))
{
//avoid traversing a previously traversed node
if (!parents.Keys.Contains(newWord))
{
parents.Add(newWord.ToString(), s);
nextFrontier.Add(newWord);
}
}
if (newWord.ToString() == end)
{
return ExtractPath(start, end);
}
}
}
}
currentFrontier.Clear();
currentFrontier.Concat(nextFrontier);
nextFrontier.Clear();
}
throw new ArgumentException("The given dictionary cannot be used to get a path from start to end");
}
private static List<string> ExtractPath(string start,string end)
{
List<string> path = new List<string>();
string current = end;
path.Add(end);
while (current != start)
{
current = parents[current];
path.Add(current);
}
path.Reverse();
return path;
}
答案 6 :(得分:0)
我认为我们不需要图表或其他复杂的数据结构。我的想法是将字典加载为HashSet
并使用contains()
方法来查明字词中是否存在该字词。
请检查此伪代码以查看我的想法:
Two words are given: START and STOP.
//List is our "way" from words START to STOP, so, we add the original word to it first.
list.add(START);
//Finish to change the word when START equals STOP.
while(!START.equals(STOP))
//Change each letter at START to the letter to STOP one by one and check if such word exists.
for (int i = 0, i<STOP.length, i++){
char temp = START[i];
START[i] = STOP[i];
//If the word exists add a new word to the list of results.
//And change another letter in the new word with the next pass of the loop.
if dictionary.contains(START)
list.add(START)
//If the word doesn't exist, leave it like it was and try to change another letter with the next pass of the loop.
else START[i] = temp;}
return list;
据我所知,我的代码应该是这样的:
输入:DAMP,LIKE
输出:DAMP,LAMP,LIMP,LIME,LIKE
输入:BACK,PICK
输出:BACK,PACK,PICK
答案 7 :(得分:0)
@Codeaddict解决方案是正确的,但是却错过了简化和优化解决方案的机会。
DFS与BFS:
如果我们使用DFS,则有可能在图中更深入地遇到target
字符串(或to_string
)。然后,我们必须跟踪找到它的级别以及对该节点的引用,最后找到可能的最小级别,然后从根目录对其进行跟踪。
例如,考虑此转换from
-> zoom
:
from
/ \
fram foom
/ \ / \
dram drom [zoom] food << To traverse upto this level is enough
... | ...
doom
|
[zoom]
使用BFS,我们可以大大简化此过程。我们要做的就是:
from
级别0
字符串开始。将此字符串添加到visitedSetOfStrings
。visitedSetOfStrings
。target
字符串,请停止对节点/字符串的进一步处理。否则,请继续执行步骤2。为了简化路径跟踪,我们可以在每个节点中添加parent
字符串的额外信息。
答案 8 :(得分:-1)
class Solution {
//static int ans=Integer.MAX_VALUE;
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
HashMap<String,Integer> h=new HashMap<String,Integer>();
HashMap<String,Integer> h1=new HashMap<String,Integer>();
for(int i=0;i<wordList.size();i++)
{
h1.put(wordList.get(i),1);
}
int count=0;
Queue<String> q=new LinkedList<String>();
q.add(beginWord);
q.add("-1");
h.put(beginWord,1);
int ans=ladderLengthUtil(beginWord,endWord,wordList,h,count,q,h1);
return ans;
}
public int ladderLengthUtil(String beginWord, String endWord, List<String> wordList,HashMap<String,Integer> h,int count,Queue<String> q,HashMap<String,Integer> h1)
{
int ans=1;
while(!q.isEmpty())
{
String s=q.peek();
q.poll();
if(s.equals(endWord))
{
return ans;
}
else if(s.equals("-1"))
{
if(q.isEmpty())
{
break;
}
ans++;
q.add("-1");
}
else
{
for(int i=0;i<s.length();i++)
{
for(int j=0;j<26;j++)
{
char a=(char)('a'+j);
String s1=s.substring(0,i)+a+s.substring(i+1);
//System.out.println("s1 is "+s1);
if(h1.containsKey(s1)&&!h.containsKey(s1))
{
h.put(s1,1);
q.add(s1);
}
}
}
}
}
return 0;
}
}
答案 9 :(得分:-2)
这显然是一种排列问题。使用图表是过度的。问题陈述缺少一个重要的约束; 您只能更改每个职位一次 。这使得它隐含了解决方案在4个步骤内。现在需要决定的是替换操作的顺序:
Operation1 =将“H”改为“T”
Operation2 =将“E”改为“A”
Operation3 =将“A”改为“I”
Operation4 =将“D改为”L“
解决方案,即操作序列,是字符串“1234”的一些排列,其中每个数字代表被替换字符的位置。例如“3124”表示首先应用operation3,然后是operation1,然后是operation2,然后是操作4.在每一步,如果结果单词不在字典中,则跳到下一个排列。合理的琐碎。编码任何人吗?