【问题标题】:How can I check to see if a pair of words are only 1 letter different?如何检查一对单词是否只有一个字母不同?
【发布时间】:2013-06-07 16:24:54
【问题描述】:

在编写比较 2 个单词的方法时,如何检查单词是否只有 1 个字母不同?我假设单词的长度和字母顺序无关紧要(参见“cobra”、“bravo”)。

def one_letter_apart?(word1, word2)

我希望得到以下结果:

one_letter_apart?("abra","abro") == true
one_letter_apart?("cobra","bravo") == true
one_letter_apart?("bravo","tabby") == false
one_letter_apart?("abc","cab") == false

我尝试了几种操作它们的方法(拆分、排序、然后设置相等并添加到新数组、然后计数),但到目前为止都没有奏效。非常感谢任何想法。

【问题讨论】:

  • 我不太清楚你的第二个问题是什么意思。可以举个例子吗?
  • 比如“abc”和“cab”,“c”从末尾移动到开头。那一个字母有区别吗?我通常不清楚要求。
  • 嗨,乔希,对于“abc”和“cab”,对于我正在考虑的问题,它们不会是一个字母不同。顺序无关紧要。
  • 为了清晰和语法,您的问题已经过大量编辑。此外,请每个问题只问一个问题。谢谢!

标签: ruby string methods


【解决方案1】:

检查 Levenshtein 距离

你想要Levenstein distance。例如,使用text gem:

require 'text'

def one_letter_apart? string1, string2
  Text::Levenshtein.distance(string1, string2).eql? 1
end

one_letter_apart? "abra", "abro"
# => true 
one_letter_apart? "cobra", "bravo"
# => false 

【讨论】:

  • 是否有不同的方法可以使“cobra”和“bravo”返回true,因为“c”和“v”是唯一不同的字母?谢谢
  • 问题中 cobra 和 bravo 是一个字母。如果我理解正确的问题,您可以通过在计算编辑距离之前对字符串进行排序来解决此问题。
  • @Frederick Cheung,拆分/排序的版本将在 ["a","b","o","r","v"] 和 ["a","b" ,"c","o","r"].
【解决方案2】:

如果两个字符串的长度相等且只有一个不同的字母,而所有其他字母都在相同的位置,则此函数返回 true:

def one_letter_apart? string1, string2
  return false if string1.size != string2.size
  found = false
  (0...string1.size).each do |i|
    next if string1[i] == string1[i]
    return false if found  # if found is already true, and we found another difference, then result is false.
    found = true  # We found the first difference.
  end
  found  # True if only one difference was found.
end

此函数也处理错误位置的字母(如“cobra”和“bravo”):

def one_letter_apart? string1, string2
  letters1 = string1.chars.each_with_object(Hash.new(0)) { |c, h| h[c] += 1 }
  letters2 = string2.chars.each_with_object(Hash.new(0)) { |c, h| h[c] -= 1 }
  diff = letters1.merge(letters2) { |key, count1, count2| count1 + count2 }
  return diff.values.select { |v| v != 0 } .sort == [-1, 1]
end

【讨论】:

  • 这对所有情况都非常干净,除了字母被打乱的情况外。 one_letter_apart 不会返回 true?("cobra","bravo")
  • @JamieS 也为这种情况添加了解决方案。
【解决方案3】:
def one_letter_apart?(s1, s2)
  return false if s1.length != s2.length

  a2 = s2.chars.to_a
  s1.chars.each do |c|
    if i = a2.index(c)
      a2.delete_at(i)
    end
  end

  a2.length == 1
end

one_letter_apart?("abra","abro") == true
# => true
one_letter_apart?("cobra","bravo") == true
# => true
one_letter_apart?("bravo","tabby") == false
# => true
one_letter_apart?("abc","cab") == false
# => true

更新:回答你关于它是如何工作的问题:这与 steenslag 的通用算法完全相同,但我没有想到使用 String#sub! 进行删除,所以我转换了到数组并使用indexdelete_at 的组合删除给定字符的第一次出现。朴素的方法是a2.delete_at(a2.index(c)),但如果a2 中不存在字符c,则index 返回nil,这是delete_at 的无效输入。解决方法是仅在index 返回非nil 的情况下调用delete_at,这就是我所做的。 i 被声明并设置为a2.index(c),并且该赋值的值由if 评估。同理:

i = a2.index(c)
if i
  # ...

我更喜欢 steenslag 的方法,如果我想到 String#sub!,我也会做同样的事情。

【讨论】:

  • 答案也很好!谢谢
  • 我无法理解变量 i 的声明位置/方式?如果您能解释一下,将不胜感激。
  • @JamieS 我的解释太长,无法评论;我已将其添加为我的答案的更新。
  • 谢谢您的解释!!
【解决方案4】:

这个利用了String#sub 仅替换它找到的第一件事这一事实。

def one_different_char?(str, other)
  other_str = other.dup
  str.chars{|char| other_str.sub!(char, '')} #sub! just replaces just one occurence of char
  other_str.size == 1
end


test_set = [["abra","abro"],["cobra","bravo"],["bravo","tabby"],["abc","cab"]]
test_set.each{|first, second| puts one_different_char?(first, second) }

#true
#true
#false
#false

【讨论】:

  • 不错!这与我的算法基本相同,但更短更简洁,因为这是删除第一次出现的更简单的方法。
  • 如果other 恰好与str 相同并附加了一个字母,则它会中断,例如one_different_char?('abc', 'abcd')。您必须检查lengths 是否等于解决此问题。 (这是假设我正确理解了要求。)
  • @DarshanComputing 仔细阅读问题。
  • @sawa 我仔细阅读了,我只是不清楚“我假设单词的长度相同并且字母顺序无关紧要”是否意味着“该方法可以假设其输入是相同的长度”或“方法必须确保其输入的长度相同”。也许只是我在不存在时看到了模棱两可,这是我们在使用自然语言时都容易出现的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-28
  • 2021-12-07
  • 1970-01-01
  • 2021-05-16
  • 2020-02-07
  • 2012-11-02
  • 2021-09-13
相关资源
最近更新 更多