Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/regex/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Regex 用于确定两个全局模式(或正则表达式)的匹配项是否相交的算法_Regex_Algorithm_String Matching - Fatal编程技术网

Regex 用于确定两个全局模式(或正则表达式)的匹配项是否相交的算法

Regex 用于确定两个全局模式(或正则表达式)的匹配项是否相交的算法,regex,algorithm,string-matching,Regex,Algorithm,String Matching,我正在寻找匹配的glob样式模式,类似于。引述: 哈罗和哈罗,哈罗,哈罗 h*llo匹配hllo和heeeello h[ae]llo匹配hello和hallo,但不匹配hillo 但我并不是在与文本字符串进行匹配,而是在两端都有意义的所有运算符的情况下,将模式与另一个模式进行匹配 例如,这些模式应在同一行中相互匹配: prefix* prefix:extended* *suffix *:extended:suffix left*right left*middle

我正在寻找匹配的glob样式模式,类似于。引述:

  • 哈罗和哈罗,哈罗,哈罗
  • h*llo匹配hllo和heeeello
  • h[ae]llo匹配hello和hallo,但不匹配hillo
但我并不是在与文本字符串进行匹配,而是在两端都有意义的所有运算符的情况下,将模式与另一个模式进行匹配

例如,这些模式应在同一行中相互匹配:

prefix*       prefix:extended*
*suffix       *:extended:suffix
left*right    left*middle*right
a*b*c         a*b*d*b*c
hello*        *ok
pre[ab]fix*   pre[bc]fix*
这些不应匹配:

prefix*       wrong:prefix:*
*suffix       *suffix:wrong
left*right    right*middle*left
pre[ab]fix*   pre[xy]fix*
?*b*?         bcb
A: *a*[bc]*?*d* --- B: dbabdfc
    ^                    ^
A: *a*[bc]*?*d* --- B: dbabdfc
      ^                   ^
A: *a*[bc]*?*d* --- B: dbabdfc
           ^               ^
A: *a*[bc]*?*d* --- B: dbabdfc
             !               
所以我想知道

  • 如果可以(实现验证算法),如果可以
  • 如果不可能,正则表达式的哪个子集是可能的?(即不允许*通配符?)
  • 如果确实可行,什么是有效的算法
  • 所需的时间复杂度是多少

编辑:已找到,但这与
hello*
*ok
匹配的单词不完全相同,它们不是彼此的子集/超集,而是相交的

所以我猜,从数学上来说,这可能是:;是否可以确定地检查一个模式匹配的一组单词与另一个模式匹配的一组单词相交是否会导致非空集合


编辑:一位朋友起草了这张排除表,清楚地显示了什么可能是潜在的/部分的解决方案:


编辑:将为那些还可以提供工作代码(任何语言)和测试用例的人增加额外的奖励



编辑:添加了?*b*?@DanielGimenez在注释中发现的测试用例。

使用非常简化的模式语言,问题中的pastebin链接和jpmc26注释几乎都在那里:主要问题是,输入字符串的文字左端和右端是否匹配。如果它们都包含,并且都至少包含一个
*
,则字符串将匹配(因为您可以始终将其他字符串与该星号匹配)。有一种特殊情况:如果其中只有一个是空的(在删除pre-和后缀之后),那么如果另一个完全由
*
s组成,那么它们仍然可以匹配

当然,在检查字符串的结尾是否匹配时,还需要考虑单字符通配符
和字符类。单字符通配符很容易:它不会失败,因为它总是匹配其他字符。如果它是一个字符类,而另一个只是一个字符,则需要检查该字符是否在该类中。如果它们都是类,则需要检查类的交集(这是一个简单的集合交集)

以下是JavaScript中的所有内容(查看代码注释,了解我上面概述的算法如何映射到代码):

这不是最整洁的实现,但它可以工作并且(希望)仍然非常可读。检查开头和结尾时会有相当多的代码重复(检查开头后,可以通过简单的
反转
来缓解这种情况,但我认为这只会让事情变得模糊)。可能还有很多其他方面可以大大改进,但我认为逻辑已经就绪

还有几点说明:实现假设模式格式良好(没有不匹配的左括号或右括号)。另外,我从中获取了数组交集代码,因为它很紧凑——如果需要,您当然可以提高它的效率

不管这些实现细节如何,我想我也可以回答您的复杂性问题:外部循环同时遍历两个字符串,一次遍历一个字符。这就是线性复杂性。循环中的一切都可以在固定时间内完成,除了字符类测试。如果一个字符是字符类而另一个不是,则需要线性时间(以类的大小为参数)来检查字符是否在类中。但这并不意味着它是二次的,因为类中的每个字符意味着外部循环的迭代次数减少了一次。所以这仍然是线性的。因此,最昂贵的事情是两个字符类的交集。这可能比线性时间更复杂,但最糟糕的情况是
O(N logn)
:毕竟,您可以对两个字符类进行排序,然后在线性时间中找到一个交集。我认为,通过将字符类中的字符散列到其Unicode代码点(
.charCodeAt(0)
在JS中)或其他数字,甚至可以获得整体线性时间复杂度,并且在线性时间内可以在散列集中找到交集。所以,如果你真的想,我想你应该能够开始做
O(N)

什么是
N
?上限是两种模式的长度之和,但在大多数情况下,它实际上会更小(取决于两种模式的前缀和后缀的长度)

请指出我的算法缺失的任何边缘情况。我也对建议的改进感到高兴,如果它们改进了或者至少没有降低代码的清晰度

(感谢chakrit将其粘贴在那里)


编辑:正如Daniel指出的,我的算法忽略了一个概念上的边缘情况。如果(在消除开头和结尾之前或之后)一个字符串不包含
*
,而另一个包含,则在某些情况下,这两个字符串仍然冲突。不幸的是,我现在没有时间调整代码片段以适应这个问题,但我可以概述如何解决它

删除字符串两端后,如果两个字符串都为空或至少包含
*
,则它们将始终匹配(查看可能的
*A: *a*[bc]*?*d* --- B: db?bdfdc
    ^                    ^
A: *a*[bc]*?*d* --- B: db?bdfdc
      ^                   ^
A: *a*[bc]*?*d* --- B: db?bdfdc
           ^               ^
A: *a*[bc]*?*d* --- B: db?bdfdc
             ^               ^
A: *a*[bc]*?*d* --- B: dbabdfc
    ^                    ^
A: *a*[bc]*?*d* --- B: dbabdfc
      ^                   ^
A: *a*[bc]*?*d* --- B: dbabdfc
           ^               ^
A: *a*[bc]*?*d* --- B: dbabdfc
             !               
function intersects(left, right) {
    var lt, rt,
        result = new CompareResult(null, null, true);

    lt = (!left || left instanceof Token) ? left : tokenize(left);
    rt = (!right || right instanceof Token) ? right : tokenize(right);

    while (result.isGood && (lt || rt)) {
        result = tokensCompare(lt, rt);

        lt = result.leftNext;
        rt = result.rightNext;
    }

    return result;
}

function tokensCompare(lt, rt) {
    if (!lt && rt) return tokensCompare(rt, lt).swapTokens();

    switch (lt.type) {
        case TokenType.Char: return charCompare(lt, rt);
        case TokenType.Single: return singleCompare(lt, rt);
        case TokenType.Set: return setCompare(lt, rt);
        case TokenType.AnyString: return anyCompare(lt, rt);
    }
}

function anyCompare(tAny, tOther) {
    if (!tOther) return new CompareResult(tAny.next, null);

    var result = CompareResult.BadResult;

    while (tOther && !result.isGood) {
        while (tOther && !result.isGood) {
            switch (tOther.type) {
                case TokenType.Char: result = charCompare(tOther, tAny.next).swapTokens(); break;
                case TokenType.Single: result = singleCompare(tOther, tAny.next).swapTokens(); break;
                case TokenType.Set: result = setCompare(tOther, tAny.next).swapTokens(); break;
                case TokenType.AnyString:
                    // the anyCompare from the intersects will take over the processing.
                    result = intersects(tAny, tOther.next);
                    if (result.isGood) return result;
                    return intersects(tOther, tAny.next).swapTokens();
            }

            if (!result.isGood) tOther = tOther.next;
        }

        if (result.isGood) {
            // we've found a starting point, but now we want to make sure this will always work.
            result = intersects(result.leftNext, result.rightNext);
            if (!result.isGood) tOther = tOther.next;
        }
    }

    // If we never got a good result that means we've eaten everything.
    if (!result.isGood) result = new CompareResult(tAny.next, null, true);

    return result;
}

function charCompare(tChar, tOther) {
    if (!tOther) return CompareResult.BadResult;

    switch (tOther.type) {
        case TokenType.Char: return charCharCompare(tChar, tOther); 
        case TokenType.Single: return new CompareResult(tChar.next, tOther.next);
        case TokenType.Set: return setCharCompare(tOther, tChar).swapTokens(); 
        case TokenType.AnyString: return anyCompare(tOther, tChar).swapTokens();
    }
}

function singleCompare(tSingle, tOther) {
    if (!tOther) return CompareResult.BadResult;

    switch (tOther.type) {
        case TokenType.Char: return new CompareResult(tSingle.next, tOther.next);
        case TokenType.Single: return new CompareResult(tSingle.next, tOther.next);
        case TokenType.Set: return new CompareResult(tSingle.next, tOther.next);
        case TokenType.AnyString: return anyCompare(tOther, tSingle).swapTokens();
    }
}
function setCompare(tSet, tOther) {
    if (!tOther) return CompareResult.BadResult;

    switch (tOther.type) {
        case TokenType.Char: return setCharCompare(tSet, tOther);
        case TokenType.Single: return new CompareResult(tSet.next, tOther.next);
        case TokenType.Set: return setSetCompare(tSet, tOther);
        case TokenType.AnyString: return anyCompare(tOther, tSet).swapTokens();
    }
}

function anySingleCompare(tAny, tSingle) {
    var nextResult = (tAny.next) ? singleCompare(tSingle, tAny.next).swapTokens() :
        new CompareResult(tAny, tSingle.next);
    return (nextResult.isGood) ? nextResult: new CompareResult(tAny, tSingle.next);
}

function anyCharCompare(tAny, tChar) {
    var nextResult = (tAny.next) ? charCompare(tChar, tAny.next).swapTokens() :
        new CompareResult(tAny, tChar.next);

    return (nextResult.isGood) ? nextResult : new CompareResult(tAny, tChar.next);
}

function charCharCompare(litA, litB) {
    return (litA.val === litB.val) ?
        new CompareResult(litA.next, litB.next) : CompareResult.BadResult;
}

function setCharCompare(tSet, tChar) {
    return (tSet.val.indexOf(tChar.val) > -1) ?
        new CompareResult(tSet.next, tChar.next) : CompareResult.BadResult;
}

function setSetCompare(tSetA, tSetB) {
    var setA = tSetA.val,
        setB = tSetB.val;

    for (var i = 0, il = setA.length; i < il; i++) {
        if (setB.indexOf(setA.charAt(i)) > -1) return new CompareResult(tSetA.next, tSetB.next);
    }
    return CompareResult.BadResult;
}
from greenery.lego import parse as p
a_z = p("[a-z]")
b_x = p("[b-x]")
assert a_z | b_x == a_z
m_n = p("m|n")
zero_nine = p("[0-9]")
assert not m_n | zero_nine == m_n