lintcode154 - Regular Expression Matching - hard

Implement regular expression matching with support for '.' and '*'.
'.' 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(string s, string p)
Example
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

DP。 O(m*n), m为string长度,n为pattern长度
1.定义:dp[i][j]是s.substring(0, i)和p.substring(0, j)是否为匹配对,从头开始对比。
2.状态转移方程:
a)dp[i][j] = dp[i - 1][j - 1]; 在两个字符相等或p的字符为’.’时。(从左上来)
b)dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (p[j - 1] == s[i] || p[j - 1] == ‘.')); 在p的字符为’*’时,而且其中一种情况需要*前面的那个字符得是’.’或者和s当前字符相同。(从上一格来和从左两格来)
c)dp[i][j] = false; 其他情况,其实也就是两个字符不相同而且p的不是特殊字符时。
3.初始化:dp[0][0]=true, 第一行也就是声明空s”"是否匹配一段段pattern的时候,如果遇到p.c ==‘*’的情况,要取左边两格的值(让a*或者.*什么的此时表示为空,从而保证”"能匹配上a*,a*b*,a*b*.*这些pattern)。第一列也就是声明空p是否匹配一段段的string,肯定都是false啊。
4.刷新方向,自上而下,从左到右。
5.具体解释:
2a)在字符串相等或者’?’被迫匹配到s的当前字符的时候,当前位置合格了,你得把这两个字符擦掉看前面的合格不合格。
2b)dp[i][j - 2]就是说把a*这种看成空的,如果看空了是true的那肯定能借鉴过来。
dp[i - 1][j]就是把a*这种看成1个或多个a,这样你就要把s里那个匹配上的a给吞掉,但是j不改变让a*保留,因为后续对比a*说不定还可以发挥用途继续吞,有没有进一步代表那是dp[i-1][j]帮你去判断的事。值得注意的是这时候能吞的情况必须要*前的那个字母符合吞吞条件呀,所以一定要和当前s[i]相同或者是万能吞吞符’.’。
2c)普通字符当前位置都匹配不上那就不可能了,你整个字符串宏观看,最后一个是普通字符而且不一样的话,前面的*?再怎么巧妙也解决不了最后不同的问题啊。

细节:
1.题意:a*这种是可以表示0,1,n个a。.*这种事可以表示0,1,n个.,所以可以变形成不同的字母如.*==ab。
2.数组大小是[s.length()+1][p.length()+1],留出上左的行列给初始化的。所以给数组赋值的下标和取substring的下标有一个offset,要小心。所以正式part循环是[1, length()],取char是substring(i - 1)。

参考视频:https://www.youtube.com/watch?v=l3hda49XcDE&t=194s

实现:

public class Solution {
    /**
     * @param s: A string 
     * @param p: A string includes "." and "*"
     * @return: A boolean
     */
    public boolean isMatch(String s, String p) {
        // write your code here
        
        boolean[][] isMatch = new boolean[s.length() + 1][p.length() + 1];
        isMatch[0][0] = true;
        
        for (int j = 1; j <= p.length(); j++) {
            if (p.charAt(j - 1) == '*' && j >= 2) {
                isMatch[0][j] = isMatch[0][j - 2];
            }
        }
        
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 1; j <= p.length(); j++) {
                if (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') {
                    isMatch[i][j] = isMatch[i - 1][j - 1];
                } else if (p.charAt(j - 1) == '*') {
                    if (j < 2) {
                        continue;
                    }
                    // 试试可能a*表示为empty的时候你就成功了。
                    isMatch[i][j] = isMatch[i][j - 2];
                    // 当a*表示为一次或多次时,你还有一次额外成功的机会。
                    if (p.charAt(j - 2) == s.charAt(i - 1) || p.charAt(j - 2) == '.')  {
                        isMatch[i][j] = isMatch[i][j] || isMatch[i - 1][j];
                    }
                }
            }
        }
        return isMatch[s.length()][p.length()];
        
    }
}

猜你喜欢

转载自www.cnblogs.com/jasminemzy/p/9577964.html
今日推荐