Раствор 1
strings = %w[ACCT ATCT AGCT]
Сначала объедините строки и сделайте хэш всех позиций для каждого символа.
joined = strings.join
positions = (0...joined.length).group_by{|i| joined[i]}
# => {"A"=>[0, 4, 8], "C"=>[1, 2, 6, 10], "T"=>[3, 5, 7, 11], "G"=>[9]}
Затем сгруппируйте индексы по их соответствующей позиции в каждой строке, удалите те, которые повторяются столько раз, сколько строк. Эта часть является вариантом алгоритма, который предлагает Йорг .
length = strings.first.length
n = strings.length
diff = Hash[*positions.map{|k, v|
[k, v.group_by{|i| i % length}.reject{|i, is| is.length == n}.keys]
}]
Это даст что-то вроде:
diff
# => {"A"=>[], "C"=>[1], "T"=>[1], "G"=>[1]}
, что означает, что «A» появляется в одинаковых позициях во всех строках, а «C», «T» и «G» отличаются в позиции 1 (отсчет начинается с 0) строк.
Если вы просто хотите узнать позиции, в которых строки различаются, выполните
diff["G"] + diff["A"] + diff["C"] + diff["T"]
# or diff["G"] + diff["A"] + diff["C"]
# => [1]
Решение 2
Обратите внимание, что, поддерживая массив индексов, где парное сравнение не удается, и продолжая добавлять к нему индексы, будет достаточно сравнения s1
с остальными (s2
, s3
, ...).
length = s1.length
diff = []
[s2, s3, ...].each{|s| diff += (0...length).reject{|i| s1[i] == s[i]}}
Более подробное объяснение
Пусть
s1 = 'GGGGGGGGG'
s2 = 'GGGCGGCGG'
s3 = 'GGGAGGCGG'
После сравнения s1
и s2
у нас есть набор индексов [3, 6]
, которые представляют их различия. Теперь, когда мы добавляем s3
к рассмотрению, не имеет значения, сравниваем ли мы его с s1
или с s2
, потому что, если s1[i]
и s2[i]
отличаются, то i
уже включено в установите [3, 6]
, поэтому не имеет значения, отличаются ли они от s3[i]
и нужно ли добавить i
в набор. С другой стороны, если s1[i]
и s2[i]
одинаковы, то также не имеет значения, какой из них мы сравниваем с s3[i]
. Следовательно, парного сравнения s1
с s2
, s3
, ... достаточно.