正则表达式匹配dp

时间:2017-07-01 22:18:38

标签: regex algorithm

我试图了解一个着名的正则表达式匹配DP算法。 以防万一,人们不知道这里的描述和算法。

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

The function prototype should be:
bool isMatch(const char *s, const char *p)
Some examples:
isMatch("aa","a") → false
isMatch("aa","aa") → true
isMatch("aaa","aa") → false
isMatch("aa", "a*") → true
isMatch("aa", ".*") → true
isMatch("ab", ".*") → true
isMatch("aab", "c*a*b") → true


static boolean isMatch(String s, String p) {
    boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
    dp[0][0] = true;
    for (int i = 1; i < dp[0].length; i++) {
        if (p.charAt(i - 1) == '*') {
            dp[0][i] = dp[0][i - 2];
        }
    }
    for (int i = 1; i < dp.length; i++) {
        for (int j = 1; j < dp[0].length; j++) {
            char schar = s.charAt(i - 1);
            char pchar = p.charAt(j - 1);
            if (schar == pchar || pchar == '.') {
                dp[i][j] = dp[i - 1][j - 1];
            } else if (pchar == '*') {
                if (schar != p.charAt(j - 2) && p.charAt(j - 2) != '.') {
                    //   - a b *
                    // - t f f f
                    // a f t f t // b != a and b != '.' thus treat b* as 0 match
                    dp[i][j] = dp[i][j - 2];
                } else {
                    //   - a b *
                    // - t f f f
                    // a f t f t
                    // b f f t t // dp[i][j - 2] 0 match or dp[i][j - 1] 1 math or dp[i - 1][j] 2+ match (not sure why)
                    dp[i][j] = dp[i][j - 2] || dp[i][j - 1] || dp[i - 1][j];
                }
            }
        }
    }
    return dp[s.length()][p.length()];
}

我理解的大部分内容,但这一部分我没有得到它

dp[i][j] = dp[i][j - 2] || dp[i][j - 1] || dp[i - 1][j];

dp[i - 1][j]人们说这将涵盖2场比赛,但不理解这一部分。有人可以解释为什么我需要检查dp[i - 1][j]吗?

2 个答案:

答案 0 :(得分:1)

我会使用更多的非正式表示法,所以请耐心等待。 首都将表示字符串(在模式中可能包括特殊字符)和小写字母,'。'和'*'将代表自己。

假设我们将Ax与Bx匹配,这是一个以A开头的字符串(它本身是一个字符串,如xyzz),以'x'结尾,其中一个模式以B开头(它本身就是一个模式,用于例如,x。*)以'x'结尾。结果与匹配A到B的结果相同(因为我们别无选择,只能将x与x匹配)。

我们可以写如下:

isMatch(Ax, Bx) = isMatch(A, B)

同样,将Ax与By匹配是不可能的。

isMatch(Ax, Bx) = false

够容易。所以这将对应于两个嵌套循环中的第一个if语句。

现在让我们以星号为例。 将Ax与By *匹配只能通过忽略y *(取零y)来完成,即将Ax与B匹配。

isMatch(Ax, By*) = isMatch(Ax, B)

但是如果y被点或x替换,则有选择。 我们将以Ax和Bx *为例。这两个选项匹配Ax到B(意味着采用零x)或匹配A到Bx *(意味着采用x,但我们仍然可以采取更多,因此模式不会改变):

isMatch(Ax, Bx*) = isMatch(Ax, B) || isMatch(A, Bx*)

在您的代码中,最后一个将转换为:

dp[i][j] = dp[i][j - 2] || dp[i - 1][j]

所以现在我想知道你的问题是否真的与dp[i][j - 1]有关,因为这会让我感到困惑。

我可能错了,但似乎没必要。

它的意思是删除星号,即改变“零或更多” “正好一个”,已经被其他两个案件所涵盖,第二个是第一个。

答案 1 :(得分:0)

此处假设字符串不包含特殊字符&#39; 。 &#39; &#39; *&#39; ,因为否则所提供的代码无法正常工作!!

dp[i][j]代表什么?

它表示如果只考虑字符串的第一个 i 字符和模式的 j 字符,它们是否匹配?

我们遇到&#39; *&#39;在模式中:

案例1:&#39;之前只占用0个字符模式中的*&#39;

&#39; *&#39; 单独并不意味着什么。它取决于其前面的特征。

dp[i][j-2]将完全忽略模式中的前一个字符,因为它只考虑第一个 j-2 字符,因此前面的字符与&#39;模式中的*&#39; 第j 字符)将被忽略。

现在,如果是&#39;之前的字符串和字符中的 ith 字符。 *&#39; 恰好与&#39;之前的相同或字符相同模式中的*&#39; &#39; 。 &#39; 然后再添加一个案例。

此处观察。* 可以匹配任何字符串

如果满足以上条件,请考虑以下情况。

案例2:继续使用&#39;之前的字符*&#39; 一次或多次。

dp[i-1][j]代表了这一点。请记住,模式中第j个字符是&#39; *&#39;

因此,如果字符串的第一个 i-1 字符与模式中的第一个 j 字符匹配,其中第j个字符为&# 39; *&#39; (这表明我们使用过&#39; *&#39; 之前的字符至少一次),然后我们可以说第一个 i < / strong>字符串中的字符与模式的第一个 j 字符匹配,因为我们已经确保 ith 字符与前面的字符匹配&#39;模式中的*&#39;

案例dp[i][j-1]是多余的,并在案例2中介绍。

注意:dp[i][j-1]

的冗余说明

dp[i][j-1]仅匹配&#39;之前的字符一次*&#39; 。它已在dp[i-1][j]中介绍。

原因:

我们知道 ith 字符匹配 j-1th 字符(请记住,我们在考虑此情况之前已经检查过)。

dp[i][j-1] = dp[i-1][j-2]已经在dp[i-1][j]的计算中考虑过了。

dp[i-1][j]更强大,因为dp[i-1][j] = dp[i-1][j-2] || dp[i-2][j]第j个字符是&#39; *&#39; 。所以这就是提供内存属性的原因,它允许我们多次使用一个字符。