LeetCode 10 正则表达式匹配,最简单易懂的动态规划

LeetCode 10 正则表达式匹配

  • 题目链接

  • 具体题目我就不在这里重复打了,这里再次说一下他的规则。

  • “ . ” 点可以被替换成任意字符

  • “ * ” 星可以重复前面的那个字符任意多次,也可以删掉前面的字符。注意:这里要说明的是如果前面是个点,也是可以被重复任意多次的,也可以被删除

上代码

  • 本题的动规思想我直接使用例子说明吧,如下:
  • 如果两个字符串 s = “abc” p = “a*b”
  • 构建二维数组,分别以s的不同位置所结尾,查看该子串能否被p的各位置结尾的子串能否匹配。因为下个位置可以使用到上个位置的结果。
  • 如:先看 a 能否被 a 匹配,再看 a 能否被 a* 匹配 再看 a 能否被 a*b 匹配,再看 ab 能否被 a 匹配…
  • 然后直接上代码吧,所有细节都在代码里
public boolean isMatch_1(String s, String p) {
        if (s == null || p == null) return false;
        /**
         * 整个dp二维数组的行变量从0开始到p的各个字符,列变量从0开始到s的各个字符
         * 如:对于s = "aab"  p = "a.*b" 组成的二维数组如下
         *       0  a  .  *  b
         *     0
         *     a
         *     a
         *     b
         */
        /**
         * 这里把p字符串放在行变量上是为了验证p字符串对s的各终结点的子串的匹配情况
         */
        boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
        //第一个,0,0点肯定为true
        dp[0][0] = true;
        //先初始化第一行,第一行因为要对空字符串匹配,所以只能由*号做删除才能匹配
        for (int i = 0; i < p.length(); i++) {
            if (p.charAt(i) == '*') {
                /**
                 *  ‘*’号可以删除前一个元素,所以查看在不包括前一个元素的情况下,能否匹配。
                 *  这里主要是初始化操作,为了让我们的p字符串匹配到一个空字符串,那么就只能使用'*'去删除他的前一个元素,看看删除之后是不是为空字符串即可
                 */
                dp[0][i + 1] = dp[0][i - 1];
            }
        }
        /**
         * 因为前面的初始化都是相当于空字符串操作的,所以这里我们遍历的开始还是从每个字符串的第一个开始。
         */
        for (int i = 0; i < s.length(); i++) {
            char sC = s.charAt(i);
            for (int j = 0; j < p.length(); j++) {
                char pC = p.charAt(j);
                if (pC == sC) {
                    /**
                     * 如果此时的p字符串所在字符与s当前位置字符相等,那么p当前位置能否匹配s当前位置取决于p的上一个位置能否匹配s的上个位置
                     */
                    /**
                     * 假设此时 i = 0  j = 0  此时的情况如下:
                     *         0  a  .  *  b
                     *      0  @
                     *      a  F ¥
                     *      a  F
                     *      b  F
                     *
                     *    注:此时 ¥ 处的值取决于 @ 处的值
                     */
                    dp[i + 1][j + 1] = dp[i][j];
                } else if (pC == '.') {
                    /**
                     * 如果此时 p 字符串当前字符是'.',因为'.'的效果是可以替换任意字符,所以也可以类比到-》此时字符与s当前字符相等,故结果同上
                     */
                    dp[i + 1][j + 1] = dp[i][j];
                } else if (pC == '*') {
                    /**
                     * 如果此时p字符串当前位置是'*',因为'*'的效果是可以把上个字符复制-1到n次,换而言之,'*'既可以把上个字符删掉,
                     * 也可以在上个字符后面再复制任意个上个字符。
                     *
                     * 这里我们要根据三种情况进行讨论,也就是p字符串的'*'之前的字符,要么与s当前位置的字符相等,要么不相等,要么为'.'
                     *
                     * 所以分以下三种情况进行判断:
                     */
                    if (p.charAt(j - 1) == sC) {
                        /**
                         * p的上个位置与s的当前位置相等,如下:
                         *      图示如下:假设此时 i = 1  j = 1
                         *         0  a  *  .  b
                         *      0  T
                         *      a  F  #
                         *      a  %  @  $
                         *      b  F
                         *  此时p位置'*',p的上一位置为'a',s 位置'a'
                         *
                         * 所以此时当以下三种情况时,$为true :
                         *     1,@为true
                         *     2,#为true
                         *     3,%为true
                         */
                        dp[i + 1][j + 1] = dp[i + 1][j] || dp[i + 1][j - 1] || dp[i][j];
                    } else if (p.charAt(j - 1) == '.') {
                        /**
                         * p的上个位置与为'.',如下:
                         *      图示如下:假设此时 i = 1  j = 2
                         *         0  a  .  *  b
                         *      0  T  &
                         *      a  F        %
                         *      a  F  #  @  $
                         *      b  F
                         *  此时p位置'*',p的上一位置为'.',s 位置'a'
                         *
                         * 所以此时当以下四种情况时,$为true :
                         *     1,@为true
                         *     2,#为true
                         *     3,%为true,这里的原因是因为前面为'.',而'*'的作用是复制前一个字符任意次,所以这里直接再复制两个'.'即可
                         *     4,当满足&位置存在时,&为true也满足条件
                         */
                        dp[i + 1][j + 1] = dp[i + 1][j] || dp[i + 1][j - 1] || dp[i][j + 1];
                        if (i >= 1) {
                            dp[i + 1][j + 1] |= dp[i - 1][j - 1];
                        }
                    } else {
                        /**
                         * p的上个位置既不为'.'也不与s的上个位置相等,如下:
                         *      图示如下:假设此时 i = 1  j = 1
                         *         0  a  c  *  b
                         *      0  T
                         *      a  F
                         *      a  F  #     $
                         *      b  F
                         *  所以此时可能匹配的情况就是'*'将前一个字符删掉,只要#位置匹配了,$也就为true了
                         */
                        dp[i + 1][j + 1] = dp[i + 1][j - 1];
                    }
                }
            }
        }
        return dp[s.length()][p.length()];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值