从零学算法10

10 .给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素

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

示例 1:

输入:s = "aa", p = "a"

输出:false

解释:"a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:s = "aa", p = "a*"

输出:true

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

示例 3:

输入:s = "ab", p = ".*"

输出:true

解释:".*" 表示可匹配零个或多个('*')任意字符('.')

  • 首先基本情况就是:我们会想到同时遍历两个字符串,s 为被匹配字符串,p 为正则字符串,在匹配过程中 s 是不断往后遍历的,但 p 不一定,因为 * 和前面的字符组合可以表示多个字符。也就是说我们的匹配过程其实可以认为是不断看 s:i 和 p:j 是否匹配(python 语法,就是其实就是字符串前 i-1 位和前 j-1 位)
  • dp 的感觉是不是就来了,从 s:1 和 p:1(即两个字符串的首位字符) 开始匹配,每次添加 s 或 p 的一个字符看是否匹配,最终得到两个完整字符串是否匹配。那么状态定义就来了,设 dpij 为 s 的前 i 个字符与 p 的前 j 个字符时候匹配。
  • 状态转移方程:因为 dp00 对应的是空字符,所以需要注意 dpij 对应的新添加的字符是 si - 1 和 pj - 1。上面提到了,匹配过程唯一的变数其实就在 p,所以以它来划分情况,找到 dpij 和 dpi-1j 或者 dpij-1 或者 dpi-1j-1 的联系。dpij 对应的是 si-1 和 pj-1,我们唯一不确定的其实就是 p 中有没有 .*,并且其实 * 包含的情况更多,那么我们就把 . 划分到不包含 * 的情况中(这里说的包含不是整个字符,而是前一个状态是否包含,具体继续往下看)。
  • 要匹配的话,首先如果 pj-1* ,以下三种情况 dpij 可以匹配成功:
    • dp[i][j-2] 为 true: 让 pj-2 的某个字符和 pj-1* 组合为该字符出现 0 次,比如 aab*aa,跳过 b*
    • dp[i-1][j] && p[j-2] == s[i-1]:没加 si-1 之前就匹配了,现在让 pj-2 和 pj-1 组合成s[i-1]*,即 p 包含一个或多个 s[i-1] ,比如 aaa 和 a* 的匹配
    • dp[i-1][j] && p[j-2] == '.':没加 si-1 之前就匹配了,现在让 pj-2 和 pj-1 组合成.*,也等于 p 包含一个或多个 s[i-1] ,比如 aaa 和 .* 的匹配
  • 如果 pj-1 不为 *,以下两种情况能匹配成功:
    • dp[i-1][j-1] && p[j-1] == s[i-1]:除了新加的两个字符,之前都匹配,且新加的两个字符为同一个字符
    • dp[i-1][j-1] && p[j-1] == '.':除了新加的两个字符,之前都匹配,且新加的 pj-1 为任意字符 .
java 复制代码
  public boolean isMatch(String s, String p) {
      int m=s.length()+1,n=p.length()+1;
      boolean[][] dp = new boolean[m][n];
      dp[0][0] = true;
      for(int j=2;j<n;j+=2)
          dp[0][j] = dp[0][j-2] && p.charAt(j - 1) == '*';
      for(int i = 1; i < m; i++) {
          for(int j = 1; j < n; j++) {
              if(p.charAt(j - 1) == '*') {
                  if(dp[i][j - 2]) dp[i][j] = true; 
                  else if(dp[i - 1][j] && s.charAt(i - 1) == p.charAt(j - 2)) 
                      dp[i][j] = true;
                  else if(dp[i - 1][j] && p.charAt(j - 2) == '.') dp[i][j] = true;
              } else {
                  if(dp[i - 1][j - 1] && s.charAt(i - 1) == p.charAt(j - 1)) 
                      dp[i][j] = true;
                  else if(dp[i - 1][j - 1] && p.charAt(j - 1) == '.') 
                      dp[i][j] = true;
              }
          }
      } 
      return dp[m-1][n-1];
  }
  • 也可以简化成三元运算和与或运算
java 复制代码
  public boolean isMatch(String s, String p) {
      int m = s.length() + 1, n = p.length() + 1;
      boolean[][] dp = new boolean[m][n];
      dp[0][0] = true;
      // 初始化首行
      for(int j = 2; j < n; j += 2)
          dp[0][j] = dp[0][j - 2] && p.charAt(j - 1) == '*';
      // 状态转移
      for(int i = 1; i < m; i++) {
          for(int j = 1; j < n; j++) {
              dp[i][j] = p.charAt(j - 1) == '*' ?
                  dp[i][j - 2] || dp[i - 1][j] && 
                  (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.') :
                  dp[i - 1][j - 1] && 
                  (p.charAt(j - 1) == '.' || s.charAt(i - 1) == p.charAt(j - 1));
          }
      }
      return dp[m - 1][n - 1];
  }
相关推荐
乐观勇敢坚强的老彭38 分钟前
C++ 竞赛常用算法模板速查表
开发语言·c++·算法
高洁0143 分钟前
工信部教考中心证书
人工智能·深度学习·算法·机器学习·知识图谱
我变成萤火虫1 小时前
河南萌新联赛2026第(三)场:郑州轻工业大学
数据结构·c++·算法·贪心算法·stl·深度优先·哈希算法
Dr.kangder1 小时前
嵌入式面试总结(十九)——内存泄露
单片机·算法·面试·职场和发展·架构·硬件架构
Herbert_hwt1 小时前
【C语言基础】常量、选择结构与运算符全解析
c语言·开发语言·算法
Suxing92 小时前
C语言基础分享:从“租房”到“拆迁”,C语言动态内存管理
java·数据结构·算法
LuminousCPP2 小时前
单链表专题(四)-刷题复盘篇-LeetCode 138 随机链表复制|原地拷贝法突破复杂指针操作
数据结构·笔记·算法·leetcode·链表
逆境不可逃3 小时前
【LeetCode 912】排序数组——随机化快速排序详解
数据结构·算法·排序算法
Coder-magician3 小时前
《代码随想录》刷题打卡day31:动态规划-背包问题part02
算法·动态规划
薛定e的猫咪3 小时前
从因果视角解决多智能体协作:细读 SCIC 算法
算法