3

2 つの単語を比較するメソッドを作成する際に、単語が 1 文字だけ異なるかどうかを確認するにはどうすればよいですか? 単語の長さは同じで、文字の順序は関係ないと仮定しています (「コブラ」、「ブラボー」を参照)。

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

それらを操作するいくつかの方法を試しました (分割、並べ替え、次に等しく設定し、新しい配列に追加してからカウントする) が、これまでのところうまくいきませんでした。どんなアイデアでも大歓迎です。

4

4 に答える 4

5

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
于 2013-06-11T18:57:05.480 に答える
5

レーベンシュタイン距離の確認

レーベンシュタイン距離が必要です。たとえば、テキスト 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 
于 2013-06-11T18:07:03.560 に答える
1
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))が、その文字cが に存在しない場合、a2は をindex返しますnil。これは の無効な入力ですdelete_at。回避策は、私が行ったことである以外の 何かを返すdelete_at場合にのみ呼び出すことです。が宣言されて に設定され、その代入の値が によって評価されます。以下と同じです:indexnilia2.index(c)if

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

私はsteenslagのアプローチをはるかに好み、もし私が考えていたらまったく同じことをしたでしょうString#sub!.

于 2013-06-11T18:55:43.183 に答える
-1

この関数は、2 つの文字列の長さが等しく、文字が 1 つだけ異なっていて、他のすべての文字が同じ位置にある場合に 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
于 2013-06-11T18:24:24.540 に答える