C++ 检查一个正则表达式是否覆盖另一个正则表达式
我正在尝试实现一个文本聚类算法。该算法通过使用正则表达式替换原始文本的相似行来对其进行聚类,并聚合匹配每个正则表达式的模式数,从而提供输入文本的整洁摘要,而不是显示输入文本中的重复模式。在这次尝试中,我遇到了寻找一个正则表达式是否覆盖另一个正则表达式的需要 假设我们只关心带有“*”和“+”通配符的正则表达式,即“*”表示字母表的零次或多次出现,“+”表示字母表的一次或多次出现。还假定字符集为ASCII 例如:C++ 检查一个正则表达式是否覆盖另一个正则表达式,c++,regex,data-mining,cluster-analysis,C++,Regex,Data Mining,Cluster Analysis,我正在尝试实现一个文本聚类算法。该算法通过使用正则表达式替换原始文本的相似行来对其进行聚类,并聚合匹配每个正则表达式的模式数,从而提供输入文本的整洁摘要,而不是显示输入文本中的重复模式。在这次尝试中,我遇到了寻找一个正则表达式是否覆盖另一个正则表达式的需要 假设我们只关心带有“*”和“+”通配符的正则表达式,即“*”表示字母表的零次或多次出现,“+”表示字母表的一次或多次出现。还假定字符集为ASCII 例如: 1. AB covers AB This is straightforwa
1. AB covers AB
This is straightforward.
2. ABC* covers ABC
Because ABC* can generate: ABC, ABCC, ABCCC etc.
3. A*B+C* covers AB+C*
Because A*B+C* can generate ABBC, AABBC, AABBCC etc. which covers
all strings generated by AB+C*.
4. A+M+BC* covers AMM+B+C+M+BC*
Similar to case [3] above.
基本上,我正在寻找以下方法的一个有效实现,它告诉我们strA(可能包含正则表达式)是否覆盖strB(可能包含正则表达式)。注意,还应该有一种方法来转义输入字符串strA和strB中的正则表达式字符“*”和“+”
C++中的方法签名:
bool isParentRegex(const string& strA, const string& strB)
我的想法是实现需要递归方法,可能有点复杂。但是我很想知道我是否可以重用现有的实现,而不是重新发明轮子,或者是否有其他简单的方法可以做到这一点。请检查,但请记住,它不会适用于所有的regexp(因为这将导致解决问题。我将执行类似于实现一个函数的操作,从给定的正则表达式中查找最小DFA DFA getminimadfa(regexr1)就是这样做的
bool isParentRegex(Regex r1, Regex r2) {
DFA a = GetMinimalDFA(r1);
DFA b = GetMinimalDFA(Regex.OR(r1,r2))
return a.Equals(b);
}
考虑到您提出的简单正则表达式语法,解决方案相当简单 举一个更复杂的例子,
A+M+BC*包括AMM+B+C+M+BC*
您可以将其重写为A{1,}M{1,}B{1,1}C{0,}
coversA{1,1}M{2,}B{1,}C{1,}M{1,}B{1,1}C{0,}
这就引出了一条简单的规则:R1
覆盖了R2
如果所有符号都以相同的顺序出现,那么R1
的所有下界都小于或等于R2
的下界,而R1
的上界则大于或等于R2
的下界
现在这个简单规则有一个小问题。AB*C
包含AC
,也就是说,有一个可选符号可能出现在R1
中,而不是R2
中。当存在一个(可选)符号时,可以通过在R2
中插入{0,0}
来解决这个问题R1中的符号不出现在R2
中的等效位置。例如AB*C
包含AB{0,0}C
“可选符号”规则是一种优化。如果
R1
中的符号不是可选的,R1
当然不包括R2
。例如AB+C
不包括AC
。因此不需要插入B{0,0}
。但是如果你这样做,你会看到a{1,1}B{1,}C{1,1}
不包括A{1,1}B{0,0}C{1,1}
,因为B
(1)上的R1
下限大于B
(0)上的R2
下限在Perl中,这将非常简单。第一步是通过将A+
更改为AA*
,A*A
更改为AA*
,以及A*A*
更改为A*
来规范每个正则表达式:
sub normalize_regex($)
{
local $_ = shift;
s/(.)\+/$1$1*/g;
1 while s/(.)\*\1(?!\*)/$1$1*/g or s/(.\*)\1/$1/g;
return $_;
}
第二步是将第一个正则表达式从与字符串本身匹配的正则表达式转换为与这些字符串匹配的规范化正则表达式匹配的Perl正则表达式;例如,AA*B
将转换为^AA**?B$
,这意味着“字符串开头,后跟A,后跟零个或多个A,可选后跟星号,后跟B,后跟字符串结尾:”
第三步无需解释:
sub does_regex1_cover_regex2($$)
{
my ($r1, $r2) = @_;
$r1 = regex_to_metaregex normalize_regex $r1;
$r2 = normalize_regex $r2;
return scalar $r2 =~ m/$r1/;
}
这将为您的案例返回一个真值#1–3。但是,它为您的案例#4返回一个假值,因为除非我真的遗漏了什么,a+m+BC*
不包括AMM+B+C+m+BC*
注意,还应该有一种方法来转义输入字符串strA和strB中的正则表达式字符“*”和“+”
在上面的代码中,我并不担心这一点,但由于您只担心ASCII,预处理步骤可以处理\*
意思是*
,\+
意思是+
,以及\\
意思是\
,方法是将它们转换为ASCII范围之外的单个字符:
sub process_escapes($)
{
local $_ = shift;
s/\\\\/\x80/g;
s/\\\+/\x81/g;
s/\\\*/\x82/g;
s/\x80/\\/g;
return $_;
}
(尽管这显然有点老套)
C++中,你可以使用同样的方法-存在库,它可以实现Perl正则表达式的所有必要特征——虽然显然它会有更多的工作。
可能的复制品和大多数事物一样,在Perl这会更容易。-):“不,这是关于重叠的(有任何输入)吗?”这是一个子集问题。最小DFA?我不确定它是否有效:最小DFA是唯一的吗?而且,计算最小DFA不一定比原来的问题简单。(事实上,我猜它更难。)(编辑:事实上,这是一个已知的问题,所以可能不是)典型的Comp.Sci.答案:我不知道这个问题的解决方案,但我可以证明它和另一个问题一样难:“:P但是的,Wiki链接显示有一个唯一的最小DFA。不过,DFA.Equals(DFA)
是另一个难用的函数。@MSalters,哦,是的,我应该读它而不是浏览它。:)停顿问题适用于图灵机:正则语言(由正则表达式描述)比TMs弱得多,因此完全有可能找到解决此问题的通用方法。Perl“regex”不是形式意义上的正则语言。请参阅。但是,是的,即使是那些“非正则正则正则表达式”“他们仍然很虚弱。我认为这比你所说的更棘手;考虑代码< > R1 < /代码>是<代码> { 1,} B { 0,} { 1,}和<代码> R2< /代码>是<代码> { 2,}。为了在正确的位置插入B{0,0}
,您需要将A{2,}
向上拆分,这需要havi
sub process_escapes($)
{
local $_ = shift;
s/\\\\/\x80/g;
s/\\\+/\x81/g;
s/\\\*/\x82/g;
s/\x80/\\/g;
return $_;
}