Regular Expression Matching解题心得

来源:互联网 发布:牛顿环实验数据参考 编辑:程序博客网 时间:2024/06/09 21:03

Regular Expression Matching解题心得

My code:https://github.com/zhanzongyuan/leetcode/blob/master/010_Regular%20Expression%20Matching.cpp
Problem source: https://leetcode.com/problems/regular-expression-matching/description/

  • Regular Expression Matching解题心得
    • 题目重述
    • 解题思路


题目重述

输入两个字符串,一个是普通字符串s,一个是简化的正则表达式r,r除了26个小写字母外还包含’.’和’*’。r里的正则表达式’x*’代表匹配多个连续的x或则0个字符,’.’则是匹配所有的字母

解题思路

  • 最初思路:使用stack的思路,从后往前逐个进行匹配,但是对于’*’和’.’的特例情况无法彻底解决,只能通过“加插件”的方式对其补充特殊情况,这种方法导致自己越补漏洞越出现新的bug。所以需要取得新的思考方向,得到一个可证明的方法。

    • 总结,以后当一个思路修修补补了很多后就不要用这个思路了,换一个试试,因为这样的方法不优雅
  • 递归思路:isMatch(ssss, x*xxx)=isMatch(ssss, xxx) | | ((s==x||x==’.’)&&isMatch(sss, x*xxx));以及isMatch(ssss, xxxx)=isMatch(sss, xxx);得到状态递归方程,通过该方程递推判断;其实可以看作一个深度搜索,对所有情况递归遍历。时间复杂度,可以对最坏情况进行分析,即正则式是这种形式’x*x*x*’,则每次递归都会进入两个子程序,考虑近似于遍历满二叉树,则树的最大深度为T+P/2,则时间复杂度O(2T+P/2);达到指数级

    • 代码部分实现如下:
      //v 1.0 Recursionclass Solution {public:    bool isMatch(string s, string p) {        if (p.size()==0) {            return s.size()==0;        }        bool first_match=s.size()!=0&&(s[0]==p[0]||p[0]=='.');        if (p.size()>=2&&p[1]=='*'){            return isMatch(s, p.substr(2)) || (first_match&&isMatch(s.substr(1), p));        }        else {            return first_match&&isMatch(s.substr(1), p.substr(1));        }    }};
  • 动态规划思路:

    • Top-Down : 类似于上面的递归思路,但是,递归的思路做了很多重复运算,所以可以用一个矩阵保存已经计算过的结果,传入每一层的递归调用中,减少不必要的重复计算。这里通过递归调用的方法实现
    • Bottom-Up:不需要进行函数自身的递归调用,通过状态转移方程可以知道,一个状态的答案由右边的状态和下一层的状态决定,应该对整个状态矩阵进行从底向上从右到左的计算,最底层的计算可以在上面几层的计算里使用到
    • Bottom-Up注意点:注意点:对于边界值的特殊情况的处理需要仔细斟酌,矩阵的两个维度可以大多一排,存特殊值。

      • 部分Bottom-Up代码实线,这里是改进版的bottom-up,是从前向后进行计算,但是思想是一样的
        //v 2.0 DP 动态规划class Solution {public:    bool isMatch(string s, string p) {        /**         * dp[i+1,j+1]: s[0:i] matchs p[0:j]         * dp[i+1,j+1]== (p[j]!='*'&&((p[j]=='.'||p[j]==s[i])&&dp[i,j]))         *           && (p[j]=='*' &&(         *           dp[i+1, j-1]                                      //x* repeat 0 times         *           || ((p[j-1]=='.'||p[j-1]==s[i])&&dp[i, j+1]))   //x* repeat >=1 times         *         * !!!对i=0 or j=0 的dp[i][j]特殊处理         * i==0 && j==0: true         * i==0 && j+1<>0: dp[0][j+1]=p[j]=='*'&&dp[0][j-1]         */        int n=s.length(), m=p.length();        vector<vector<bool>> dp(n+1, vector<bool>(m+1, false));        dp[0][0]=true;        for(int j=1; j<m; j++){            dp[0][j+1]=p[j]=='*'&&dp[0][j-1];        }        for(int i=1; i<n; i++){            dp[i][0]=false;        }        for (int i=0; i<n; i++){            for (int j=0; j<m; j++){                if (p[j]!='*'){                    dp[i+1][j+1]=(p[j]=='.'||p[j]==s[i])&&dp[i][j];                }                else{                    dp[i+1][j+1]=dp[i+1][j-1]||((p[j-1]=='.'||p[j-1]==s[i])&&dp[i][j+1]);                }            }        }        return dp[n][m];    }};
    • dp的时间复杂度显而易见即矩阵遍历的时间复杂度O(TP),相对于递归的指数级时间复杂度优化很多
原创粉丝点击