【问题标题】:Comparing strings of equal lengths and noting where the differences occur比较相等长度的字符串并注意差异出现的位置
【发布时间】:2011-04-29 07:22:06
【问题描述】:

给定两个长度相等的字符串

s1 = "ACCT"
s2 = "ATCT"

我想找出字符串不同的位置。所以我做了这个。 (请提出更好的方法。我打赌应该有)

z= seq1.chars.zip(seq2.chars).each_with_index.map{|(s1,s2),index| index+1 if s1!=s2}.compact

z 是两个字符串不同的位置数组。在这种情况下 z 返回 2

假设我添加了一个新字符串

s3 = "AGCT"

我希望将它与其他字符串进行比较,看看 3 个字符串的不同之处。我们可以采用与上述相同的方法,但这次

s1.chars.zip(s2.chars,s3.chars)

返回一个数组数组。给定两个字符串,我只是在比较两个字符是否相等,但是当我添加更多字符串时,它开始变得不堪重负,并且随着字符串变得更长。

#=> [["A", "A", "A"], ["C", "T", "G"], ["C", "C", "C"], ["T", "T", "T"]]

跑步

s1.chars.zip(s2.chars,s3.chars).each_with_index.map{|item| item.uniq}

 #=> [["A"], ["C", "T", "G"], ["C"], ["T"]] 

可以帮助减少冗余并返回完全相同的位置(大小为 1 的非空子数组)。然后我可以打印出大小 > 1 的子数组的索引和内容。

s1.chars.zip(s2.chars,s3.chars,s4.chars).each_with_index.map{|item| item.uniq}.each_with_index.map{|a,index| [index+1,a] unless a.size== 1}.compact.map{|h| Hash[*h]}
#=> [{2=>["C", "T", "G"]}]

我觉得随着字符串数量的增加和字符串长度的增加,它会停止或变慢。有哪些最佳方法可以做到这一点? 谢谢你。

【问题讨论】:

标签: ruby algorithm string


【解决方案1】:

这是我要开始的地方。我故意使用不同的字符串以便更容易看到差异:

str1 = 'jackdaws love my giant sphinx of quartz'
str2 = 'jackdaws l0ve my gi4nt sphinx 0f qu4rtz'

获取第一个字符串的字符:

str1.chars.with_index.to_a - str2.chars.with_index.to_a
=> [["o", 10], ["a", 19], ["o", 30], ["a", 35]]

获取第二个字符串的字符:

str2.chars.with_index.to_a - str1.chars.with_index.to_a
=> [["0", 10], ["4", 19], ["0", 30], ["4", 35]]

随着字符串变大,速度会有点慢,但不会很糟糕。


编辑:添加了更多信息。

如果您有任意数量的字符串,并且需要比较它们,请使用Array#combination

str1 = 'ACCT'
str2 = 'ATCT'
str3 = 'AGCT'

require 'pp'

pp [str1, str2, str3].combination(2).to_a
>> [["ACCT", "ATCT"], ["ACCT", "AGCT"], ["ATCT", "AGCT"]]

在上面的输出中,您可以看到 combination 循环遍历数组,返回数组元素的各种 n 大小的组合。

pp [str1, str2, str3].combination(2).map{ |a,b| a.chars.with_index.to_a - b.chars.with_index.to_a }
>> [[["C", 1]], [["C", 1]], [["T", 1]]]

使用组合的输出,您可以循环遍历数组,将所有元素相互比较。因此,在上面返回的数组中,在“ACCT”和“ATCT”对中,“C”是两者之间的差异,位于字符串中的位置 1。类似地,在“ACCT”和“AGCT”中,差异再次是“C”,在位置 1。最后对于 'ATCT' 和 'AGCT',它在位置 1 处是 'T'。

因为我们已经在较长的字符串示例中看到代码将返回多个更改的字符,所以这应该让您非常接近。

【讨论】:

  • 你会建议在给定超过 2 个字符串时进行成对比较吗?假设您有 3 或 4 个字符串。
  • 谢谢!很接近了。我很感激!
  • ruby 2.1.x 使用中没有with_index 方法。您可以使用each_with_index 方法来完成相同的操作。
  • @atmosx:2.1+ 什么时候没有with_index 方法了?在 2.1.4 中,with_index 存在于 ruby-doc.org/core-2.1.4/Enumerator.html#method-i-with_index 中。你觉得each_with_index是怎么实现的?
  • @theTinMan:嗯,你说得对。昨天我需要实现类似的东西,所以我使用上面的代码with_index 出现unknown methodeach_with_index 工作正常,所以我想我应该分享信息,但现在我不确定为什么会发生这种情况。我正在使用 ruby​​-2.1.2p195 (rvm)。
【解决方案2】:

解决方案 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]}

然后,根据每个字符串中的相应位置对索引进行分组,删除那些重复次数与字符串数量相同的索引。这部分是an algorithm that Jorg suggests的变体。

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 与其余的(s2s3、...)进行比较就足够了。

length = s1.length
diff = []
[s2, s3, ...].each{|s| diff += (0...length).reject{|i| s1[i] == s[i]}}

更详细的解释

假设

s1 = 'GGGGGGGGG'
s2 = 'GGGCGGCGG'
s3 = 'GGGAGGCGG'

在比较s1s2 之后,我们有一组索引[3, 6] 表示它们的不同之处。现在,当我们将s3 加入考虑范围内时,将其与s1s2 进行比较并不重要,因为如果s1[i]s2[i] 不同,那么i 已经包含在设置[3, 6],因此它们中的任何一个是否与s3[i] 不同都没有区别,并且i 将被添加到集合中。另一方面,如果s1[i]s2[i] 相同,那么我们将其中一个与s3[i] 进行比较也没有区别。因此,s1s2, s3, ... 的成对比较就足够了。

【讨论】:

    【解决方案3】:

    您几乎肯定不想使用自己的代码进行此分析。相反,您希望将其交给现有的multiple sequence alignment 工具,例如Clustal

    我知道这不是您问题的答案,但我希望它是您问题的解决方案!

    【讨论】:

    • 抱歉,这可能不是我问题的答案。一方面,多重对齐假设一些替换函数和一个空位惩罚并假设一个进化历史。如果字符串没有这样的关系并且我不希望进行替换假设,这将是错误的。 :)
    • 您始终可以使用对所有替换进行同等加权的替换矩阵。你仍然会有空档罚分;如果你不想使用间隙,你可以把它做得非常大,这样就永远不会打开任何间隙。
    猜你喜欢
    • 2020-12-07
    • 2016-02-04
    • 2017-04-03
    • 2021-04-19
    • 1970-01-01
    • 1970-01-01
    • 2018-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多