6-正则表达式匹配

该博客讨论了如何实现支持'.'和'*'的正则表达式匹配算法,通过动态规划解决匹配整个字符串的问题。文章通过示例解释了'.'和'*'的含义,并详细阐述了解决方案,包括不同情况下字符串和字符规律的匹配逻辑。

题目

给你一个字符串 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
解释:".
” 表示可匹配零个或多个(’*’)任意字符(’.’)。

示例 4:

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

示例 5:

输入:s = “mississippi” p = “misisp*.”
输出:false

题解

class Solution {
    public boolean isMatch(String s, String p) {
        int m=s.length();
        int n=p.length();
        boolean[][] dp=new boolean[m+1][n+1];//dp[i][j]代表第i个a和第j个b是否匹配
        dp[0][0]=true;
        for(int j=1;j<=n;j++){//a为空,b的匹配情况,遇*直接跨越
            if(p.charAt(j-1)=='*'){
                dp[0][j]=dp[0][j-2];
            }
        }
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(p.charAt(j-1)=='*'){
                    if(p.charAt(j-2)=='.'||p.charAt(j-2)==s.charAt(i-1)){//有*时p前移两格匹配,s只要前移一格,注意:s移的是i!!
                        dp[i][j]=dp[i-1][j]||dp[i][j-2];
                    }else{
                        dp[i][j]=dp[i][j-2];
                    }
                }else{
                    if(p.charAt(j-1)==s.charAt(i-1)||p.charAt(j-1)=='.'){
                        dp[i][j]=dp[i-1][j-1];
                    }
                }
            }
        }
        return dp[m][n];
    }
}

思路:

  1. 以一个例子详解动态规划转移方程:
    S = abbbbc
    P = ab*d*c
    1. 当 i, j 指向的字符均为字母(或 '.' 可以看成一个特殊的字母)时,
       只需判断对应位置的字符即可,
       若相等,只需判断 i,j 之前的字符串是否匹配即可,转化为子问题 f[i-1][j-1].
       若不等,则当前的 i,j 肯定不能匹配,为 false.
       
           f[i-1][j-1]   i
                |        |
       S [a  b  b  b  b][c] 
       
       P [a  b  *  d  *][c]
                         |
                         j
       
    
    2. 如果当前 j 指向的字符为 '*',则不妨把类似 'a*', 'b*' 等的当成整体看待。
       看下面的例子
    
                i
                |
       S  a  b [b] b  b  c  
       
       P  a [b  *] d  *  c
                |
                j
       
       注意到当 'b*' 匹配完 'b' 之后,它仍然可以继续发挥作用。
       因此可以只把 i 前移一位,而不丢弃 'b*', 转化为子问题 f[i-1][j]:
       
             i
             | <--
       S  a [b] b  b  b  c  
       
       P  a [b  *] d  *  c
                |
                j
       
       另外,也可以选择让 'b*' 不再进行匹配,把 'b*' 丢弃。
       转化为子问题 f[i][j-2]:
    
                i
                |
       S  a  b [b] b  b  c  
        
       P [a] b  *  d  *  c
          |
          j <--
    
    3. 冗余的状态转移不会影响答案,
       因为当 j 指向 'b*' 中的 'b' 时, 这个状态对于答案是没有用的,
       原因参见评论区 稳中求胜 的解释, 当 j 指向 '*' 时,
       dp[i][j]只与dp[i][j-2]有关, 跳过了 dp[i][j-1].
    
  2. 归纳:
    如果 p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1];
    如果 p.charAt(j) == '.' : dp[i][j] = dp[i-1][j-1];
    如果 p.charAt(j) == '*':
    	如果 p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] //in this case, a* only counts as empty
    	如果 p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.':
    	dp[i][j] = dp[i-1][j] //in this case, a* counts as multiple a
    	or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a
    	or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty
    
  3. 如果a串为空,b串不空,则看看是否有带*的情况,有的话就b前移两个

    如果ab都不空,则也分两种情况:如果b有xing*,则检查b的前一个字母和a是否相同,或者b前一个是不是.,两者满足一个都可以,dp就更新到或者跳过a的那个字母,或者跳过b的两个字母;如果b无xing*,则检查两个字符串的前一个字母是否相同,相同则前移。

  4. 之所以可以在j-1的循环里用j-2是因为*前面肯定是跟着别的字母的,*不会在最前面。

<think>首先,用户的问题是:“我想查找使用正则表达式匹配其他正则表达式匹配结果的方法 请问正则表达式匹配其他正则表达式匹配结果的方法”,翻译成英文是:“I want to find a method to use regex to match the results of other regex matches. How do I match regex results with regex?” 用户提到了几个引用,我需要参考它们。引用[1]是关于匹配浮点数的正则表达式,引用[2]是关于匹配IP地址和一些其他模式的正则表达式,引用[3]是关于匹配${xxx}这样的模板字符串正则表达式。 用户的核心问题是:如何用正则表达式匹配其他正则表达式匹配结果。这意味着,用户可能有一个文本,其中包含一些已经由另一个正则表达式匹配的部分,用户想用一个新的正则表达式匹配那些特定的匹配结果。 在正则表达式中,匹配其他正则表达式匹配结果通常不是一个直接的功能,因为正则表达式是用于匹配文本模式的,而不是匹配其他匹配结果。但是,用户可能在问如何捕获和处理子匹配,或者在嵌套匹配上下文中使用正则表达式。 回顾引用[3],它讨论了匹配${xxx},并改进正则表达式来处理空格和操作符。这表明用户可能对捕获组和精确匹配感兴趣。 可能的解释: 1. **嵌套匹配**:用户可能想在一个文本中匹配某个模式,然后对那些匹配到的部分应用另一个正则表达式。这不是用单个正则表达式完成的,而是需要编程实现的:先用第一个正则表达式匹配所有位置,然后对每个匹配结果应用第二个正则表达式。 2. **引用捕获组**:在正则表达式中,捕获组可以存储部分匹配,用户可能想匹配这些捕获组的值。但正则表达式本身不能动态引用先前匹配的结果(除非在替换部分使用反向引用)。 用户说“匹配其他正则表达式匹配结果”,所以更可能指的是处理第一个正则表达式的输出。 在回答中,我需要: - 使用系统级指令:所有行内数学表达式用$...$,独立公式用$$...$$。 - 使用中文回答。 - 生成相关问题。 - 引用末尾添加引用标识。 结构化回答: 1. **解释问题**:澄清正则表达式匹配结果的本质。 2. **方法**:描述如何在编程中实现匹配匹配结果。 - 步骤1:使用第一个正则表达式扫描文本,获取所有匹配对象或字符串- 步骤2:对每个匹配的对象或字符串应用第二个正则表达式。 3. **举例**:基于引用来举例,比如引用[3]中的${xxx}匹配- 例如:首先用引用[3]的正则匹配所有${...},然后对每个匹配应用另一个正则表达式提取内容。 4. **正则表达式本身**:如果用户想在单个正则表达式中实现,可以使用捕获组和贪婪/非贪婪匹配,但会有限制。 在引用[3]中,它使用了\$\{(.*?)\}来匹配${和}之间的内容,然后可以进一步处理匹配的内容。 回答核心内容: - 正则表达式匹配结果是文本片段,因此可以用另一个正则表达式匹配它们。 - 在代码中,通常是先收集所有匹配,然后迭代处理。 生成相关问题: 1. 如何在Python中使用正则表达式处理嵌套匹配? 2. 正则表达式中捕获组的应用方法? 3. 如何处理复杂的文本模板匹配? 确保回答真实可靠。 最终回答大纲: - 介绍:正则表达式匹配结果是字符串,因此可以用其他正则表达式匹配- 详细方法:描述编程实现。 - 代码示例:使用Python的re模块,因为引用中有Python。 - 引用相关部分:比如引用[3]。 - 相关问题。 </think>### 正则表达式匹配其他正则表达式匹配结果的方法 在使用正则表达式时,匹配其他正则表达式匹配结果通常不是直接通过单个正则表达式完成的,因为正则表达式本身是静态模式匹配工具,无法直接动态引用其他匹配的文本片段。相反,这需要结合编程逻辑实现:**先用第一个正则表达式提取所有匹配结果(即文本片段),然后对这些片段应用第二个正则表达式进行进一步匹配**。这种方法常用于处理嵌套模式或复杂文本解析,例如从模板字符串中提取数据(如引用[3]中的${xxx}示例)。以下是详细解释和实现方法。 #### 核心概念 - **匹配结果本质**:正则表达式匹配结果是文本字符串(例如,从 "${bizDate+1}" 中提取的 "bizDate+1")。这些字符串可作为新正则表达式的输入。 - **编程实现流程**: 1. **步骤1: 应用第一个正则表达式**——扫描原始文本,获取所有匹配对象或匹配字符串的列表。 2. **步骤2: 应用第二个正则表达式**——对每个匹配结果(文本片段)应用第二个正则表达式,提取或验证子内容。 - **为什么不能单次完成**:正则表达式匹配是贪婪或非贪婪的模式搜索(如引用[3]使用非贪婪模式 `.*?`),但无法在单个表达式中直接“匹配匹配结果”。这需要迭代处理(常见于编程语言如Python、Java)。 #### 实现方法:以Python为例 以下代码演示如何先匹配引用[3]中的${xxx}模式,然后对每个匹配结果应用第二个正则表达式(例如提取变量名和操作符)。假设原始文本是SQL语句:`update demo1 set ptime=${bizDate+1} and date=${ bizDate }`。 ```python import re # 原始文本 text = "update demo1 set ptime=${bizDate+1} and date=${ bizDate }" # 步骤1: 应用第一个正则表达式匹配所有${xxx}片段(基于引用[3]的改进版) first_regex = r'\$\{\s*(\w+)\s*(([\+\-])\s*(\d+)\s*)?\}' # 匹配 ${xxx} 格式,并捕获内部内容 [^3] matches = re.finditer(first_regex, text) # 获取匹配迭代器 # 步骤2: 对每个匹配结果应用第二个正则表达式 second_regex = r'^(\w+)\s*([\+\-])?\s*(\d+)?$' # 针对内部内容的模式,提取变量名、操作符、数字 results = [] for match in matches: inner_content = match.group(0) # 完整匹配文本,如 "${bizDate+1}" clean_content = inner_content.strip('${}').strip() # 去除 ${} 符号,如 "bizDate+1" # 应用第二个正则表达式到清理后的内容 sub_match = re.match(second_regex, clean_content) if sub_match: variable = sub_match.group(1) # 提取变量名,如 "bizDate" operator = sub_match.group(2) if sub_match.group(2) else None # 提取操作符,如 "+" number = sub_match.group(3) if sub_match.group(3) else None # 提取数字,如 "1" results.append({ "full_match": inner_content, "variable": variable, "operator": operator, "number": number }) # 输出结果 print(results) # 示例输出: [{'full_match': '${bizDate+1}', 'variable': 'bizDate', 'operator': '+', 'number': '1'}, ...] ``` #### 关键点说明 1. **第一个正则表达式的作用**: - 使用 `\$\{\s*(\w+)\s*(([\+\-])\s*(\d+)\s*)?\}` 匹配 ${xxx} 格式(引用[3]的改进版)。这个表达式: - 匹配 `${` 和 `}`。 - 内部捕获变量名(`(\w+)`)和可选的操作符+数字(`(([\+\-])\s*(\d+)\s*)?`)。 - 非贪婪匹配确保处理空格(如 `${ bizDate }`)[^3]。 - 在代码中,`re.finditer` 返回所有匹配对象,便于后续处理。 2. **第二个正则表达式的作用**: - 针对清理后的内容(如 "bizDate+1"),使用 `^(\w+)\s*([\+\-])?\s*(\d+)?$` 进一步提取: - `^(\w+)`:匹配变量名开头。 - `\s*([\+\-])?`:可选的操作符(+或-)。 - `\s*(\d+)?$`:可选的数字结尾。 - 这允许对匹配的子内容进行精细化解析(例如区分变量和操作)。 3. **适用场景**: - 文本模板处理(如SQL、配置文件中提取动态变量)。 - 日志解析(先匹配时间戳,再匹配其中的日期部分)。 - 数据清洗(如先匹配浮点数,再提取其整数部分——引用[1]的正则可用于第一步)[^1]。 #### 注意事项 - **性能优化**:对于大文本,避免在循环中频繁编译正则表达式(可预编译:`re.compile`)。 - **工具支持**:在支持正则的编辑器或语言(如Python、JavaScript)中,此方法高效;正则表达式本身无法递归匹配- **局限性**:复杂嵌套(如正则匹配正则表达式)可能需要解析器而非纯正则,但上述方法是实用折中方案。 通过这种两步法,您可以高效匹配和处理其他正则表达式匹配结果[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codrab

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值