如何在许多正则表达式键上快速进行字符串匹配?

时间:2013-01-19 19:33:27

标签: c regex algorithm search

我有一个元素数组,其键是 regex 。 我想提出一种快速算法,给出一个字符串(不是 regex )将在少于 O(N)时间内返回什么是基于匹配的数组值执行密钥正则表达式

目前我对数组执行线性扫描,对于每个元素,我使用posix regexec API执行相应的 regex ,但这意味着找到匹配的元素我必须搜索整个阵列。

我理解如果aray仅由简单字符串组成,我可以保留orderer并使用 bsearch 样式API,但使用 regex 看起来并不那么容易。

我在这里错过了什么吗?

示例如下

// this is mainly to be considered
// as pseudocode
typedef struct {
  regex_t  r;
  ... some other data
} value;

const char *key = "some/key";
value my_array[1024];
bool  my_matches[1024];
for(int i =0; i < 1024; ++i) {
  if(!regexec(&my_array[i].r, key, 0, 0, REG_EXTENDED))
    my_matches[i] = 1;
  else
    my_matches[i] = 0;
}

但正如你所看到的,上面是线性的。 感谢

附录

我已经整理了一个简单的可执行文件,它执行了上面的算法和下面的答案提出的建议,其中形成一个大正则表达式它构建了一个子正则表达式的二叉树并导航它以找到所有的比赛 源代码在这里(GPLv3):http://qpsnr.youlink.org/data/regex_search.cpp
编译:{{1​​}}
并运行:g++ -O3 -o regex_search ./regex_search.cpp -lrt(或使用./regex_search "a/b"标记选项)

有趣的是(我会说期望)在树中搜索时,执行 regex 的次数更少,但这些要复杂得多每次比较,最终需要时间与向量的线性扫描平衡。结果打印在--help上,以便您可以看到它们是相同的。

使用长字符串和/或许多令牌运行时,请注意内存使用情况;准备点击std::cerr以阻止它阻止系统崩溃。

1 个答案:

答案 0 :(得分:1)

这是可能的,但我认为您需要编写自己的正则表达式库来实现它。

由于您正在使用posix regexen,我将假设您打算实际使用正则表达式,而不是现代正则表达式库倾向于实现的随机计算功能集合。正则表达式在union(以及许多其他操作)下关闭,因此您可以从正则表达式数组中构造单个正则表达式。

每个正则表达式都可以被DFA(确定性有限状态自动机)识别,而DFA - 无论多么复杂 - 在时间上线性地识别(或无法识别)字符串与字符串的长度成线性关系。给定一组DFA,您可以构造一个联合DFA,它可以识别所有DFA的语言,而且(略微修改DFA接受字符串的含义),您可以恢复有关哪个DFA子集的信息。 DFA匹配字符串。

我将尝试在DFA上使用与Wikipedia article相同的术语。假设我们有一组共享单个字母表Σ的DFA M = {M1...Mn}。所以我们有:

Mi = (Qi, Σ, δi, qi0, Fi) where Qi = {qij} for 0 ≤ j < |Qi|, and Qi ⊂ Fi.

我们构建了union-DFA M = (Q, Σ, δ, q0)(是的,没有F;我将会这样做),如下所示:

q0 = <q10,...,qn0>

每个δ(<q1j1,...,qnjn>, α) = <δ1(q1j1, α),... , δn(qnjn, α)>

α ∈ Σ

Q包含从δ开始q0可以到达的所有州。

我们可以使用标准闭包算法在与δi过渡函数大小的乘积成比例的时间内计算出来。

现在要对字符串α 1 ...α m 进行联合匹配,我们以通常的方式运行联合DFA,从其开始符号开始依次将其过渡函数应用于每个α。一旦我们读取了字符串中的最后一个符号,DFA将处于某种状态<q1j1,...,qnjn>。从该状态开始,我们可以提取与该字符串匹配的Mi集合:{Mi | qiji ∈ Fi}

为了使其工作,我们需要完成各个DFA(即,它们从每个符号的每个状态转换)。一些DFA构造算法产生的DFA缺少某些符号的转换(表示该语言中没有带该前缀的字符串);必须使用不接受的“接收”状态来扩充此类DFA,该状态在每个符号上都会自行转换。

我不知道有任何正则表达式库充分暴露其DFA以实现上述算法,但编写一个不试图实现任何非常规功能的简单正则表达式库并不是太多工作。您也可以找到DFA库。

从正则表达式构造DFA可能是表达式大小的指数,尽管这种情况很少见。 (非确定性FA可以在线性时间内构建,但在某些情况下,NFA上的powerset构造将需要指数时间和空间。请参阅维基百科文章。)然而,一旦构建了DFA,工会FA就可以了及时构建与DFA大小的乘积成比例。

因此,通过将每个正则表达式编译为DFA一次,并维护一组DFA,应该很容易允许动态修改正则表达式集。当正则表达式集发生更改时,只需重新生成union DFA。

希望一切都有所帮助。