使用双数组前缀树来优化字符串匹配

本文探讨了在大量字符串匹配时,如何通过构建DoubleArrayTrie优化Aho-Corasick算法,减少预处理时间和内存占用。通过实例展示了DoubleArrayTrie的构建过程及其在数据匹配中的应用,说明了其在内存效率和匹配速度上的优势。
摘要由CSDN通过智能技术生成

使用前缀树优化字符串匹配

  • 传统的字符串匹配有很多的优化算法,包括大名鼎鼎的KMP算法,算法复杂度为O(M + N).
  • 对于多路字符串匹配(Multi-Way Pattern Matching),比较出名的算法是 Aho-Corasick 算法。算法根据需要匹配的patterns创建类似于一棵 Trie-Tree 结构,且优化了在算法匹配不上的时候,节点之间的失效转移函数(failure function). 有兴趣的可以看一下一篇 中文博客

在这里插入图片描述

  • AC 算法的效率确实非常快,但是在构建AC 自动机的时候,需要创建好失效函数对应的节点,其算法复杂度可能会导致预处理的时间过长。此时如果对于少量的文本进行匹配的时候就不划算了~~所以放弃失效函数的预处理
  • 在测试的时候发现,当对1M多的pattern数据预处理之后,整个Trie-Tree的内存占用空间达到了100M+,所以考虑使用 DoubleArrayTrieTree 对TrieTree 进行优化,压缩内存占用空间
  • 实际在使用 DoubleArrayTrieTree 对程序进行优化的时候,整体的TrieTree 都构建算法复杂度可控,内存空间占用 17M,数据膨胀也不是特别厉害~~
    在这里插入图片描述

使用 TrieTree 优化字符串匹配的过程

  • 阶段1: 将我们需要匹配的字符串构建出一个TrieTree
  • 阶段2: 根据输入的字符串的每一个字符开始一次到树中进行匹配,并汇总匹配结果
    在这里插入图片描述

DoubleArrayTrieTree 优化TrieTree 存储

DoubleArrayTrieTree 算法核心是使用 Base 和 Check 两个数组对Trie 进行压缩,节约存储空间。

DoubleArrayTrieTree 构建过程

  • 将Tree重所有节点进行编码
    在这里插入图片描述

  • 设置Base[0] = 1, 并在 Base[ 1 + id(node) ] 的位置处插入节点D ,C,并设置Check=parentNode
    在这里插入图片描述

  • Base[ parentNode + Base(parentNode) + id(node) ] 的位置处插入节点E, O,并设置Check=parentNode。因为O 是 DO 的叶子结点,所以用 -1 表示
    在这里插入图片描述

  • Base[ parentNode + Base(parentNode) + id(node) ] 的位置处插入节点 A,并设置Check=parentNode
    在这里插入图片描述

  • Base[ parentNode + Base(parentNode) + id(node) ] 的位置处插入节点G,并设置Check=parentNode
    在这里插入图片描述

  • Base[ parentNode + Base(parentNode) + id(node) ] 的位置处插入节点E( 这里包括了一次对数组的扩容 ),并设置Check=parentNode
    在这里插入图片描述

  • Base[ parentNode + Base(parentNode) + id(node) ] 的位置处插入节点T,并设置Check=parentNode,Base(6) = -1,表示为叶子结点
    在这里插入图片描述

  • Base[ parentNode + Base(parentNode) + id(node) ] 的位置处插入节点S,并设置Check=parentNode,Base(22) = -1,表示为叶子结点
    在这里插入图片描述

  • Base[ parentNode + Base(parentNode) + id(node) ] 的位置处插入节点P,并设置Check=parentNode,Base(29) = -1,表示为叶子结点
    在这里插入图片描述

  • 上面的算法中并没有发生冲突,所以过程中的 Base(parentNode) =1 或 -1, 如果我们选取的Base位置已经存在了节点,则需要使用启发性算法找到一个合适的 Base(parentNode) 值,来使得 parentNode 的所有子节点对应的Base 位都没有冲突

使用DoubleArrayTrieTree 进行数据匹配过程

Input字符串: I love cats and dogs

  • I = 0, 字母 I 在 Dict 中匹配不上,跳过
  • I = 1, 字母 ’ ’ 在 Dict 中匹配不上,跳过
  • I = 2, 字母 l 在 Dict 中匹配不上,跳过
  • I = 3, 字母 o 在 Dict 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 4) = Base(5) 值为 0, 表示这个位置没有对应节点,所以匹配失败
  • I = 4, 字母 v 在 Dict中匹配不上,跳过
  • I = 5, 字母 e 在 Dict 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 7) = Base(8) 值为 0, 表示这个位置没有对应节点,所以匹配失败
  • I = 6, 字母 ’ ’ 在 Dict 中匹配不上,跳过
  • I = 7, 字母 c 在 Dict 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 0) = Base(1) 值为 1, 且check(1) = 0 表示节点确实存在,且父节点为 0
    • 匹配第二个字符 a 在Dict 中存在。查找 Base[parentNode + Base(parentNode) + id(node)] = Base(1 + 1 + 1) = Base(3) 值为 1, 且check(1) = 1 表示节点确实存在,且父节点为 1
    • 匹配第三个字符 t 在 Dict 中存在。查找 Base[parentNode + Base(parentNode) + id(node)] = Base(3 + 1 + 2) = Base(6) 值为 -1, 且check(1) = 3 表示节点确实存在,且父节点为 3。 因为Base值为负值,表示是叶子结点,即’CAT’ pattern匹配上。
  • I = 8, 字母 a 在 Dict 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 1) = Base(2) 值为 0, 表示这个位置没有对应节点,所以匹配失败
  • I = 9, 字母 t 在 Dict 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 2) = Base(3) 值为 1, 表示这个位置有节点。但是check(3) = 1 !=0 表示这个节点不是节点(0) 的子节点,所以匹配失败
  • I = 10, 字母 ’ ’ 在Dict 中匹配不上,跳过
  • I = 11, 字母 d 在Dict 中匹配存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 3) = Base(4) 值为 1, 且check(1) = 0 表示节点确实存在,且父节点为 0
    • 匹配第二个字符 o 在Dict 中存在。查找 Base[parentNode + Base(parentNode) + id(node)] = Base(4 + 1 + 4) = Base(9) 值为 -1, 且check(9) = 4 表示节点确实存在,且父节点为 4. 因为Base值为负值,表示是叶子结点, 所以存储匹配结果: DO
    • 匹配第三个字符 g 在Dict 中存在。查找 Base[parentNode + Base(parentNode) + id(node)] , 因为Base(9) 为负值,所以使用它的绝对值 = Base(9 + abs(-1) + 5) = Base(15) 值为 -1, 且check(15) = 9 表示节点确实存在,且父节点为 9. 因为Base值为负值,表示是叶子结点, 所以存储匹配结果: DOG
    • 匹配第四个字符 s 在 Dict 中存在。查找 Base[parentNode + Base(parentNode) + id(node)] , 因为Base(15) 为负值,所以使用它的绝对值 = Base(15 + abs(-1) + 6) = Base(22) 值为 -1, 且check(22) = 15 表示节点确实存在,且父节点为 15. 因为Base值为负值,表示是叶子结点, 所以存储匹配结果: DOGS
  • I = 12, 字母 o 在 Dict 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 4) = Base(5) 值为 0, 表示这个位置没有对应节点,所以匹配失败
  • I = 13, 字母 g 在 Dict 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 5) = Base(6) 值为 -1, 表示这个位置有节点。但是check(6) = 3 != 0 表示这个节点不是节点(0) 的子节点,所以匹配失败
  • I = 14, 字母 s 在DIct 中存在。
    • 查找 Base[parentNode + Base(parentNode) + id(node)] = Base(0 + 1 + 6) = Base(7) 值为 0, 表示这个位置没有对应节点,所以匹配失败
  • 汇总所有输出结果,去除重复的值(同一个pattern只需要被统计一次)后进行输出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值