字符串中的模式的符号表示,并找到“相似的”子模式

时间:2012-09-11 16:41:03

标签: string algorithm data-structures pattern-matching

字符串“abab”可以被认为是索引符号“0101”的模式。字符串“bcbc”也将由“0101”表示。这非常漂亮,可以进行强有力的比较,但它很快就会脱离完美的情况。

“babcbc”将是“010202”。如果我想要注意它包含一个等于“0101”(bcbc部分)的模式,我只能考虑在每个索引处进行某种规范化处理,以便从符号上“重新表示”从n到长度的子字符串进行比较。如果我试图看看“babcbc”和“dababd”(010202 vs 012120)是否有任何共同之处,那就变得复杂了。这么低效!

如何有效地完成这项工作,处理所有可能的嵌套案例?请注意,我正在寻找类似的模式,而不是实际文本中的类似子字符串。

3 个答案:

答案 0 :(得分:1)

尝试用min替换每个字符(K,回到之前出现的那个字符的距离),其中K是可调谐常数,因此babcbc和dababd变成类似KK2K22和KKK225的东西。您可以使用后缀树或后缀数组在转换后的文本中查找重复。

答案 1 :(得分:0)

您的算法因压缩字符串的原始数据集而丢失了信息,所以我不确定您是否可以恢复完整的信息集而不需要比原始字符串做更多的工作。此外,虽然您的数据集对于人类可读性而言更容易显示,但它当前占用的空间与原始字符串一样多,并且字符串的差异映射(其中值是先前字符与当前字符之间的距离)可能具有更可比较的信息集。

但是,关于如何检测所有常见子集,您应该查看最小公共子序列算法以找到最大匹配模式。它是一个定义良好的算法,效率很高--O(n * m),其中n和m是字符串的长度。请参阅LCS on SOWikipedia。如果你还想看到围绕一个字符串的模式(作为圆形搅拌 - abeabeabab应该匹配)那么你需要一个ciruclar LCS,在{I篇论文中描述{3}}

到目前为止,您需要稍微更改算法以考虑变化的数量。我的建议是在LCS表中添加两个额外的维度,表示两个原始字符串的过去k个字符中遇到的唯一数字的数量,以及每个字符串的压缩版本。然后你可以做一个LCS求解,你总是朝着压缩字符串上匹配的方向移动,并匹配过去k个字符的两个字符串中相同数量的唯一字符。这应该编码所有可能的唯一子串匹配。

棘手的部分将始终选择最大化k的方向,其中包含相同数量的唯一字符。因此,在LCS表的每个元素处,您将有一个额外的字符串搜索k值的最佳步长。由于较长的序列总是包含所有可能较小的序列,如果你在每个步骤中最大化你的选择,你知道下一次迭代的最佳k最多只有1步之遥,所以一旦填充了4D表,它应该是以与原始LCS表类似的方式解决。请注意,因为你有一个4D表,逻辑会变得更复杂,但如果你读LCS的工作方式,你将能够看到如何定义一致的规则,以便在每一步向左上角移动。因此,LCS算法保持不变,只是缩放到更多维度。

这个解决方案一旦完成就非常复杂,所以你可能想重新考虑你想要实现的目标/如果这个模式在开始编写这样的算法之前对你真正想要的信息进行编码。

答案 2 :(得分:0)

这是一个使用Prolog的统一功能和属性变量来匹配模板的解决方案:

:-dynamic pattern_i/3.

test:-
  retractall(pattern_i(_,_,_)),
  add_pattern(abab),
  add_pattern(bcbc),
  add_pattern(babcbc),
  add_pattern(dababd),
  show_similarities.

show_similarities:-
  call(pattern_i(Word, Pattern, Maps)),
  match_pattern(Word, Pattern, Maps),
  fail.
show_similarities.

match_pattern(Word, Pattern, Maps):-
  all_dif(Maps), % all variables should be unique
  call(pattern_i(MWord, MPattern, MMaps)),
  Word\=MWord,
  all_dif(MMaps),
  append([_, Pattern, _], MPattern), % Matches patterns
  writeln(words(Word, MWord)),
  write('mapping: '),
  match_pattern1(Maps, MMaps). % Prints mappings

match_pattern1([], _):-
  nl,nl.
match_pattern1([Char-Char|Maps], MMaps):-
  select(MChar-Char, MMaps, NMMaps),
  write(Char), write('='), write(MChar), write(' '),
  !,
  match_pattern1(Maps, NMMaps).

add_pattern(Word):-
  word_to_pattern(Word, Pattern, Maps),
  assertz(pattern_i(Word, Pattern, Maps)).

word_to_pattern(Word, Pattern, Maps):-
  atom_chars(Word, Chars),
  chars_to_pattern(Chars, [], Pattern, Maps).

chars_to_pattern([], Maps, [], RMaps):-
  reverse(Maps, RMaps).
chars_to_pattern([Char|Tail], Maps, [PChar|Pattern], NMaps):-
  member(Char-PChar, Maps),
  !,
  chars_to_pattern(Tail, Maps, Pattern, NMaps).
chars_to_pattern([Char|Tail], Maps, [PChar|Pattern], NMaps):-
  chars_to_pattern(Tail, [Char-PChar|Maps], Pattern, NMaps).

all_dif([]).
all_dif([_-Var|Maps]):-
  all_dif(Var, Maps),
  all_dif(Maps).

all_dif(_, []).
all_dif(Var, [_-MVar|Maps]):-
  dif(Var, MVar),
  all_dif(Var, Maps).

算法的想法是:

  • 对于每个单词生成一个未绑定变量列表,其中我们对单词中的相同char使用相同的变量。例如:对于单词abcbc,列表看起来像[X,Y,Z,Y,Z]。这定义了此单词的模板
  • 一旦我们获得了模板列表,我们就会采用每个模板并尝试将模板与每个其他单词的子模板统一起来。因此,例如,如果我们有单词abcbc和zxzx,则模板将是[X,Y,Z,Y,Z]和[H,G,H,G]。然后在第一个模板上有一个子模板,它与第二个单词的模板统一(H = Y,G = Z)
  • 对于每个模板匹配,我们显示所需的替换(变量重命名)以产生该匹配。所以在我们的例子中,替换将是z = b,x = c

测试结果(单词abab,bcbc,babcbc,dababd):

?- test.

words(abab,bcbc)
mapping: a=b b=c 

words(abab,babcbc)
mapping: a=b b=c 

words(abab,dababd)
mapping: a=a b=b 

words(bcbc,abab)
mapping: b=a c=b 

words(bcbc,babcbc)
mapping: b=b c=c 

words(bcbc,dababd)
mapping: b=a c=b