我手头有一个复杂的字符串操作问题。 我有一个字符串,其中我将有循环,以及我需要识别和列出的重现。
'abcabcabcabcabcdkkabclilabcoabcdieabcdowabcdppabzabx'
以下是可能的模式 - >
未使用的实际索引
abc - > 0,3,6,9,12,15,17,.....(重复串的出现指数), 0,3,6,9(重复字符串的unique_occurence索引,12,15,17 被取消资格,因为
abc
是较长重复子字符串的一部分)abcd - > 12,15,17(重复弦的出现指数),12,15,17 (重复出现的字符串的唯一出现指数)
bcda - > 13,16,18 ..(重现字符串的出现指数),(重复字符串的唯一出现指数),因为它是重叠的 字符串abcd 因此它不是必需的东西ab - > 0,3,6,9,12,15,17,25,27 ...(重现字符串的出现指数), 25,27(重复出现的字符串的唯一出现指数)。 .....
我想找到所有unique recurring occurences/recurrences
,即重复字符串的所有唯一,非重叠值。正如刚才提到的。输入字符串可能包含
所有循环模式(abcabcabcdefdefdeflkjlkjlkj
=> abc
,def
,lkj
都是循环中的重复,但是bc
,ab
, bcab
不是预期的,因为它们是误报的结果)
OR
单独重复出现的模式(abcxabcdabcm
=> abc
是重复但不是循环,即它们不是adjecent)
或者
两者的混合(abcabcabcabcabclkabcdokabcdhuabcd
=> abc
是循环重复,abcd
是非循环重复,我们需要同时找到 - > {{1 },abcd
是经常性的,而不是abc
,bc
,ab
等等。
有人可以为此问题陈述提出解决方案算法。我正在尝试使用suffix_arrays,它也没有找到重叠的结果。
答案 0 :(得分:2)
构造一个哈希,其键包含给定字符串的所有唯一子串,这些子串在字符串中至少出现两次(不重叠),并且对于每个键,该值是字符串中所有偏移的数组,其中值为键(子串)开始。
<强>代码强>
def recurring_substrings(str)
arr = str.chars
(1..str.size/2).each_with_object({}) do |n,h|
arr.each_cons(n).map { |b| b.join }.uniq.each do |s|
str.scan(Regexp.new(s)) { (h[s] ||= []) << Regexp.last_match.begin(0) }
end
end.reject { |_,v| v.size == 1 }
end
<强>实施例强>
recurring_substrings 'abjkabrjkab'
#=> {"a"=>[0, 4, 9], "b"=>[1, 5, 10], "j"=>[2, 7], "k"=>[3, 8], "ab"=>[0, 4, 9],
# "jk"=>[2, 7], "ka"=>[3, 8], "jka"=>[2, 7], "kab"=>[3, 8], "jkab"=>[2, 7]}
recurring_substrings "abcabcabcabcabcdkkabclilabcoabcdieabcdowabcdppabzabx"
#=> {"a"=>[0, 3, 6, 9, 12, 18, 24, 28, 34, 40, 46, 49],
# "b"=>[1, 4, 7, 10, 13, 19, 25, 29, 35, 41, 47, 50],
# "c"=>[2, 5, 8, 11, 14, 20, 26, 30, 36, 42], "d"=>[15, 31, 37, 43],
# "k"=>[16, 17], "l"=>[21, 23], "i"=>[22, 32], "o"=>[27, 38], "p"=>[44, 45],
# "ab"=>[0, 3, 6, 9, 12, 18, 24, 28, 34, 40, 46, 49],
# "bc"=>[1, 4, 7, 10, 13, 19, 25, 29, 35, 41], "ca"=>[2, 5, 8, 11],
# "cd"=>[14, 30, 36, 42],
# "abc"=>[0, 3, 6, 9, 12, 18, 24, 28, 34, 40], "bca"=>[1, 4, 7, 10],
# "cab"=>[2, 5, 8, 11], "bcd"=>[13, 29, 35, 41],
# "abca"=>[0, 6], "bcab"=>[1, 7], "cabc"=>[2, 8], "abcd"=>[12, 28, 34, 40],
# "abcab"=>[0, 6], "bcabc"=>[1, 7], "cabca"=>[2, 8],
# "abcabc"=>[0, 6], "bcabca"=>[1, 7], "cabcab"=>[2, 8]}
<强>解释强>
对于上面的第一个例子,步骤如下。
str = 'abjkabrjkab'
arr = str.chars
#=> ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]
q = str.size/2 # max size for string to repeat at least once
#=> 5
b = (1..q).each_with_object({})
#=> #<Enumerator: 1..5:each_with_object({})>
我们可以通过将它转换为数组来查看此枚举器将生成哪些元素。 (我将在下面再做几次。)
b.to_a
#=> [[1, {}], [2, {}], [3, {}], [4, {}], [5, {}]]
空哈希将在计算进度时建立。
接下来将第一个元素传递给块,并使用 parallel assignment (有时称为多个赋值)将块变量设置为它。
n,h = b.next
#=> [1, {}]
n #=> 1
h #=> {}
c = arr.each_cons(n)
#=> #<Enumerator: ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]:each_cons(1)>
c
是长度为1的所有子串的数组。在下一次迭代中,它将是长度为2的所有子串的数组,依此类推。请参阅Emumerable#each_cons。
c.to_a # Let's see which elements will be generated.
#=> [["a"], ["b"], ["j"], ["k"], ["a"], ["b"], ["r"], ["j"], ["k"], ["a"], ["b"]]
d = c.map { |b| b.join }
#=> ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]
e = d.uniq
#=> ["a", "b", "j", "k", "r"]
在下一次迭代中,这将是
r = arr.each_cons(2)
#=> #<Enumerator: ["a", "b", "j", "k", "a", "b", "r", "j", "k", "a", "b"]:
# each_cons(2)>
r.to_a
#=> [["a", "b"], ["b", "j"], ["j", "k"], ["k", "a"], ["a", "b"],
# ["b", "r"], ["r", "j"], ["j", "k"], ["k", "a"], ["a", "b"]]
s = r.map { |b| b.join }
#=> ["ab", "bj", "jk", "ka", "ab", "br", "rj", "jk", "ka", "ab"]
s.uniq
#=> ["ab", "bj", "jk", "ka", "br", "rj"]
继续,
f = e.each
#=> #<Enumerator: ["a", "b", "j", "k", "r"]:each>
f.to_a # Let's see which elements will be generated.
#=> ["a", "b", "j", "k", "r"]
s = f.next
#=> "a"
r = (Regexp.new(s))
#=> /a/
str.scan(r) { (h[s] ||= []) << Regexp.last_match.begin(0) }
如果h
还没有密钥s
,h[s] #=> nil
。 h[s] ||= []
扩展为h[s] = h[s] || []
,在执行h[s]
之前将h[s] << Regexp.last_match.begin(0)
转换为空数组。也就是h[s] = h[s] || [] #=> nil || [] #=> []
。
在块中,使用类方法MatchData检索Regexp::last_match对象。 (或者,可以用全局变量$~
替换Regexp.last_match
。有关详细信息,请在Regexp搜索“特殊全局变量”。)MatchData#begin返回JSchemaValidatingReader
的索引1}}当前比赛开始。
现在
str
其余的计算类似,将键值对添加到h #=> {"a"=>[0, 4, 9]}
,直到构造了示例中给出的值为止。
答案 1 :(得分:1)
在@ CarySwoveland的优秀答案之后进行进一步处理:
def ignore_smaller_substrings(hash)
found_indices = []
new_hash = {}
hash.sort_by{|s,_| [-s.size,s]}.each{|s,indices|
indices -= found_indices
found_indices |= indices
new_hash[s]=indices unless indices.empty?
}
new_hash
end
pp ignore_smaller_substrings(recurring_substrings('abcabcabcabcabcdkkabclilabcoabcdieabcdowabcdppabzabx'))
哈希按字符串长度减少排序(然后按字母顺序排序),索引只允许出现一次。
输出
{"abcabc"=>[0, 6],
"bcabca"=>[1, 7],
"cabcab"=>[2, 8],
"abcd"=>[12, 28, 34, 40],
"abc"=>[3, 9, 18, 24],
"bca"=>[4, 10],
"bcd"=>[13, 29, 35, 41],
"cab"=>[5, 11],
"ab"=>[46, 49],
"bc"=>[19, 25],
"cd"=>[14, 30, 36, 42],
"b"=>[47, 50],
"c"=>[20, 26],
"d"=>[15, 31, 37, 43],
"i"=>[22, 32],
"k"=>[16, 17],
"l"=>[21, 23],
"o"=>[27, 38],
"p"=>[44, 45]}
它没有完全回答这个问题,但它更接近了。