10. Regular Expression Matching

来源:互联网 发布:校招的数据分析岗多吗 编辑:程序博客网 时间:2024/03/28 22:11
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(const char *s, const char *p)Some examples:isMatch("aa","a") → falseisMatch("aa","aa") → trueisMatch("aaa","aa") → falseisMatch("aa", "a*") → trueisMatch("aa", ".*") → trueisMatch("ab", ".*") → trueisMatch("aab", "c*a*b") → true

这题首先审题得审一会, “.”的使用非常简单,就是可以匹配任意单一字符。“*”的使用是本题的关键,题目说星号可以匹配0个或者多个它的前一项字符,0个就是说星号加上它的前一项字符后整体从p字符串中消除,1个或者多个就是说星号加上它的前一项可以等于一个或者多个它的前一项字符。比如给出的例子的最后一个,之所以是true就是因为,“c*” 代表0个c, 然后“a*”代表两个a。所以才和aab匹配。理解了这一点,就明白这个字符串匹配的难点就在于星号所带来的不确定性。

这一题还是典型的递推关系,所以可以用递归recursion也可以用动态规划dynamic programming,当然后者在效率上具有明显的优势。但两种方法在基本思路的分析上还是一致的。以动态规划为主,介绍分析思路dp[i][j]表示s的前i位和p的前j位的匹配结果;总长度为dp[s.length+1][p.length+1]。
明显电平dp[0][0] = true.
1. 边界条件
i=0, j如果是奇数,就是false,如果是偶数且为*,就是true,因为偶数位如果都是*, 可以都消去。
j=0, i只能为0,否则就是false。
2. 递推关系
判断p的当前位:
p[j-1]如果是普通字符,那么如果s[i-1]==p[j-1], 此时dp[i][j] = dp[i-1][j-1];
p[j-1]如果是“.”, 那么dp[i][j] = dp[i-1][j-1];
p[j-1]如果是“*”,这种情况最为复杂,因为星号会带来匹配长度的不确定,那就只能分类讨论,如果匹配0个,那么dp[i][j-2]应该为true,这样才有意义,如果匹配一个,那么dp[i][j-1]应该为true,这样星号加前一项还是前一项,如果匹配多个,那么证明这一个星号已经在之前匹配过了,所以dp[i-1][j]应该为true,而且此时dp[i][j]如果想要为true,s[i-1]必须等于p[j-2]也就是星号的前一项,或者这个前一项为“.”。

方法一:动态规划dynamic programming

class Solution {public:    bool isMatch(string s, string p) {        int lens = s.size();        int lenp = p.size();        // 如果p为空,s必须为空        if (lenp == 0) return lens == 0;        int i, j;        bool dp[lens+1][lenp+1];        //初始化        dp[0][0] = true;        for (i = 1; i < lens+1; i++) {            dp[i][0] = false;        }        for (j = 1; j < lenp+1; j++) {            if (j > 1 && p[j-1] == '*') dp[0][j] = dp[0][j-2];            else dp[0][j] = false;        }        //递推        for (i = 1; i < lens+1; i++) {            for (j = 1; j < lenp+1; j++) {                if (p[j-1] != '*' && p[j-1] != '.') {                    if (s[i-1] == p[j-1]) dp[i][j] = dp[i-1][j-1];                    else dp[i][j] = false;                }                else if (p[j-1] == '.') dp[i][j] = dp[i-1][j-1];                else if (p[j-1] == '*' && j > 1) {                    // *对应0个前字符                    if (dp[i][j-2] == true) dp[i][j] = true;                    // *对应1个前字符                    else if (dp[i][j-1] == true) dp[i][j] = true;                    // *对应多个前字符                    else if (dp[i-1][j] == true && (s[i-1] == p[j-2] || p[j-2] == '.')) dp[i][j] = true;                    else dp[i][j] = false;                }            }        }        return dp[lens][lenp];    }};

方法二:递归recursion

class Solution {public:    bool isMatch(string s, string p) {        if (s.length() == 0){            // s串匹配完合法的情况只有p为空,或是 "X*X*"的形式            if (p.length() & 1) return false;            else {                for (int i = 1; i < p.length(); i += 2) {                    if (p[i] != '*') return false;                }            }            return true;        }        if (p.length() == 0) return false;        if (p.length() > 1 && p[1] == '*') {            //首项存在多个匹配的问题            if (p[0] == '.' || s[0] == p[0]) {                // match一个或者多个 || match0个                return isMatch(s.substr(1), p) || isMatch(s, p.substr(2));            } else return isMatch(s, p.substr(2)); //match0个        } else {            //首项没有多个匹配的问题,直接检测并跳过            if (p[0] == '.' || s[0] == p[0]) {                return isMatch(s.substr(1), p.substr(1));            } else return false;        }    }};
1 0
原创粉丝点击