找到无符号向量的所有部分匹配

时间:2016-05-24 09:21:57

标签: c++ algorithm pattern-matching

对于我的AI项目,我需要向一个因子状态应用适用于其部分组件的所有规则。这需要经常进行,所以我正在寻找一种尽可能快的方法。

我将用字符串描述我的问题,但真正的问题与无符号整数向量的工作方式相同。

我有一堆像这样的长度为N的条目,我需要以某种方式存储:

__a_b
c_e__
___de
abcd_
fffff
__a__

我的输入是单个条目ciede,我必须尽快找到所有与之匹配的存储条目。例如,在这种情况下,匹配将是c_e_____de。应该支持删除和添加条目,但我并不关心它有多慢。我想尽快做到的是:

for ( const auto & entry : matchedEntries(input) )

正如我所说,我的问题是每个字母实际上是无符号整数,并且向量具有未指定(但已知)的长度。我没有要求如何存储条目,或者要将哪种类型的元数据与它们相关联。匹配all的天真算法是O(N),是否可以做得更好?我需要存储的合理条目数是< = 100k。

我认为某种排序可能会有所帮助,或者看起来有些奇怪的树状结构,但我似乎无法找到解决这个问题的好方法。它看起来像处理器已经需要做的事情,所以有人可能会提供帮助。

3 个答案:

答案 0 :(得分:3)

最简单的解决方案是构建包含条目的trie。搜索trie时,从根开始并递归跟随边缘,该边缘与输入中的字符匹配。每个节点中最多有两个边缘,一个用于通配符_,另一个用于实际字母。

在最坏的情况下,你必须遵循每个节点的两条边,这将增加O(2 ^ n)复杂度,其中n是输入的长度,而空间复杂度是线性的。

另一种方法是预处理条目,以允许线性搜索。这基本上是正则表达式的编译。对于您的示例,请考虑使用符合所需输入的正则表达式:

(..a.b|c.e..|...de|abcd.|fffff|..a..)

该表达式可以实现为nondeterministic finite state automaton,初始状态具有ε-移动到每个单个条目的确定性自动机。然后可以使用标准powerset construction将此NFSA转换为确定性FSA。

虽然这种结构可以大大增加状态数,但是可以在线性时间内搜索输入字,只需模拟确定性自动机。

以下是条目aba_ba_a__的示例。首先从一个非确定性自动机开始,它在移除ε-移动并加入等效状态时实际上是该集合的特里。

enter image description here

然后将其转换为确定性机器,其状态对应于NFSA的状态子集。从状态0开始,对于除_之外的每个边,创建下一个状态作为原始机器中状态的并集,可以从当前集中的任何状态到达。

例如,当DFSA处于状态16时,这意味着NFSA可以处于状态16。在a转换后,NFSA可以转到状态3(来自1),78(来自6) - 将成为你在DFSA的下一个州。

标准构造将保留_ - 边,但只要输入不包含_,我们就可以省略它们。

现在,如果您在输入上有一个单词ab,则可以模拟此自动机(即遍历其过渡图)并最终处于状态238,您可以从中轻松恢复原始条目。

答案 1 :(得分:1)

将数据存储在树中,第1层表示第1个元素(字符或整数),依此类推。这意味着树在您的示例中将具有恒定的深度5(不包括根)。此时不要关心通配符(" _")。只需像其他元素一样存储它们。

搜索匹配项时,通过广度优先搜索遍历树并动态构建结果集。每当遇到通配符时,请在结果集中为该层的所有其他不匹配的节点添加另一个元素。如果没有子节点匹配,请从结果集中删除该条目。

在构建树时,您还应该跳过reduntant条目:在您的示例中,__a_b是reduntant,因为只要匹配,__a__也匹配。

答案 2 :(得分:0)

我有一个算法,我计划实施和基准测试,但我已经描述了这种方法。它需要<filter>位存储(因此对于长度为100的100k模板和256个不同的符号需要2.56 Gb = 320 MB的RAM。除非使用succinct data structure,否则这不能很好地扩展到大量符号。

查询需要n_templates * template_length * n_symbols次,但由于按位操作,它应该表现得非常好。

假设我们有一组给定的模板:

O(n_templates * template_length * n_symbols)

符号集是__a_b c_e__ ___de abcd_ _ied_ bi__e ,对于每个符号,我们预先计算一个位掩码,指示模板是否与该位置的符号不同:

abcdei

以二进制表示的相同表:

aaaaa    bbbbb    ccccc    ddddd    eeeee    iiiii
....b    ..a..    ..a.b    ..a.b    ..a.b    ..a.b
c.e..    c.e..    ..e..    c.e..    c....    c.e..
...de    ...de    ...de    ....e    ...d.    ...de
.bcd.    a.cd.    ab.d.    abc..    abcd.    abcd.
.ied.    .ied.    .ied.    .ie..    .i.d.    ..ed.
bi..e    .i..e    bi..e    bi..e    bi...    b...e

这些以柱状顺序存储,64个模板/无符号整数。要确定哪些模板与aaaaa bbbbb ccccc ddddd eeeee iiiii 00001 00100 00101 00101 00101 00101 10100 10100 00100 10100 10000 10100 00011 00011 00011 00001 00010 00011 01110 10110 11010 11100 11110 11110 01110 01110 01110 01100 01010 00110 11001 01001 11001 11001 11000 10001 匹配,我们会检查ciede表的第1列,c的第2列,i的第3列,依此类推:

e

我们发现匹配的模板为零行,表示没有找到差异。我们可以一次检查64个模板,算法本身非常简单(类似python的代码):

         ciede  ciede
__a_b    ..a.b  00101
c_e__    .....  00000
___de    .....  00000
abcd_    abc..  11100
_ied_    .....  00000
bi__e    b....  10000

正如我所说,我还没有真正尝试过这个,所以我不知道它在实践中的速度有多快。