zoukankan      html  css  js  c++  java
  • [LeetCode] 10. 正则表达式匹配

    传送门:[LeetCode] 10. 正则表达式匹配

    题目描述

    给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。

    '.' 匹配任意单个字符
    '*' 匹配零个或多个前面的那一个元素

    所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

    说明:

    • s 可能为空,且只包含从 a-z 的小写字母。
    • p 可能为空,且只包含从 a-z 的小写字母,以及字符 .*

    示例 1:

    输入:
    s = "aa"
    p = "a"
    输出: false
    解释: "a" 无法匹配 "aa" 整个字符串。

    示例 2:

    输入:
    s = "aa"
    p = "a*"
    输出: true
    解释: 因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。

    示例 3:

    输入:
    s = "ab"
    p = ".*"
    输出: true
    解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

    示例 4:

    输入:
    s = "aab"
    p = "c*a*b"
    输出: true
    解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。

    示例 5:

    输入:
    s = "mississippi"
    p = "mis*is*p*."
    输出: false

    分析与代码

    • 先忽略正则符号,两个普通字符串比较进行匹配。

      public boolean isMatch(String s, String p) {
          if (s.length() != p.length()) {
              return false;
          }
          for (int i = 0; i < p.length(); i++) {
              if (s.charAt(i) != p.charAt(i)) {
                  return false;
              }
          }
          return true;
      }
      
    • 写成递归。

      public boolean isMatch(String s, String p) {
          if (p.isEmpty()) {
              return s.isEmpty();
          }
          boolean first_match = (!s.isEmpty() && s.charAt(0) == p.charAt(0));
          return first_match && isMatch(s.substring(1), p.substring(1));
      }
      
    • 处理.,.可匹配任意单个字符。

      public boolean isMatch(String s, String p) {
          if (p.isEmpty()) {
              return s.isEmpty();
          }
          boolean first_match = (!s.isEmpty()
                                 && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'));
          return first_match && isMatch(s.substring(1), p.substring(1));
      }
      
    • 处理*,*匹配任意个前一个字符,只对当前递归而言,判断匹配 0 次还是 1 次。

      public boolean isMatch(String s, String p) {
          if (p.isEmpty()) {
              return s.isEmpty();
          }
          boolean first_match = (!s.isEmpty()
                                 && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'));
          if (p.length() >= 2 && p.charAt(1) == '*') {
              return isMatch(s, p.substring(2)) // 匹配 0 次。
                  || first_match && isMatch(s.substring(1), p); // 匹配 1 次,不需移动 p,可匹配多次。
          } 
          return first_match && isMatch(s.substring(1), p.substring(1));
      }
      

    代码:

    解法一:回溯法

    class Solution {
        public boolean isMatch(String s, String p) {
            if (p.isEmpty()) {
                return s.isEmpty();
            }
            boolean first_match = (!s.isEmpty()
                                   && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'));
            if (p.length() >= 2 && p.charAt(1) == '*') {
                return isMatch(s, p.substring(2))
                    || first_match && isMatch(s.substring(1), p);
            }
            return first_match && isMatch(s.substring(1), p.substring(1));
        }
    }
    

    解法二:动态规划优化

    class Solution {
        public boolean isMatch(String s, String p) {
            boolean[][] dp = new boolean[s.length() + 1][p.length() + 1];
            dp[s.length()][p.length()] = true;
            for (int i = s.length(); i >= 0; i--) {
                for (int j = p.length() - 1; j >= 0; j--) {
                    boolean first_match = (i < s.length()
                                           && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'));
                    if (j < p.length() - 1 && p.charAt(j + 1) == '*') {
                        dp[i][j] = dp[i][j + 2] || first_match && dp[i + 1][j];
                    } else {
                        dp[i][j] = first_match && dp[i + 1][j + 1];
                    }
                }
            }
            return dp[0][0];
        }
    }
    

    小结

    复杂问题要先从简单入手,解决了简单的问题,再在此基础框架上逐渐组装新的逻辑,最终解决复杂问题。

    重叠子问题分析:假设一个子问题,若有两条不同路径能达到子问题,则有重叠子问题,重叠子问题使用动态规划优化。



    ┆ 然 ┆   ┆   ┆   ┆ 可 ┆   ┆   ┆ 等 ┆ 暖 ┆
    ┆ 而 ┆ 始 ┆   ┆   ┆ 是 ┆ 将 ┆   ┆ 你 ┆ 一 ┆
    ┆ 你 ┆ 终 ┆ 大 ┆   ┆ 我 ┆ 来 ┆   ┆ 如 ┆ 暖 ┆
    ┆ 没 ┆ 没 ┆ 雁 ┆   ┆ 在 ┆ 也 ┆   ┆ 试 ┆ 这 ┆
    ┆ 有 ┆ 有 ┆ 也 ┆   ┆ 这 ┆ 会 ┆   ┆ 探 ┆ 生 ┆
    ┆ 来 ┆ 来 ┆ 没 ┆   ┆ 里 ┆ 在 ┆   ┆ 般 ┆ 之 ┆
    ┆   ┆   ┆ 有 ┆   ┆   ┆ 这 ┆   ┆ 降 ┆ 凉 ┆
    ┆   ┆   ┆ 来 ┆   ┆   ┆ 里 ┆   ┆ 临 ┆ 薄 ┆
  • 相关阅读:
    Javescript基础api实现原理
    React Fiber
    ASP.NET中Form验证登录后反复跳转回登录页面的问题
    跨域部署Silverlight时需要注意的问题
    Windows 8 x64+Ruby 2上安装Sqlite3方法
    .NET 项目在源码控制中程序集的引用问题
    [EntLib]解决The type or namespace name 'Data' does not exist in the namespace 'Microsoft.Practices.EnterpriseLibrary' 的错误
    ASP.NET MVC Url中参数过长引发的问题
    Windows 8 x64 QQ2012/2013beta无法启动屌丝解决方法
    TechEd 2010参会小记
  • 原文地址:https://www.cnblogs.com/qiu_jiaqi/p/LeetCode-10.html
Copyright © 2011-2022 走看看