字符串-正则表达式匹配-JZ52

描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但是与"aa.a"和"ab*a"均不匹配
示例1

输入: “aaa”,“a*a”
返回值: true

思路
假设主串为 str,模式串为 pattern 从最后一步出发,需要关注最后进来的字符。假设 str 的长度为 n ,pattern的长度为 m ,关注正则表达式 pattern的最后一个字符是谁,它有三种可能,正常字符、’*’ 和 ‘.’(点),那针对这三种情况讨论即可,如下:

  1. 如果 pattern的最后一个字符是正常字符,那就是看str[n−1] 是否等于 pattern[m−1],相等则看str [0…n−2]与 pattern [0…m−2],不等则是不能匹配,这就是子问题。
  2. 如果 pattern 的最后一个字符是 ‘.’,它能匹配任意字符,直接看 str [0…n−2]与 pattern [0…m−2]
  3. 如果 pattern 的最后一个字符是‘*’,它代表 pattern[m-2]=c 可以重复0次或多次,它们是一个整体 c*
    情况一:str[n-1] 是 0 个 c,pattern最后两个字符废了,能否匹配取决于 str [0…n−1]和 pattern [0…m−3]是否匹配
    情况二:str[n-1] 是多个 c 中的最后一个(这种情况必须 str[n-1]=c 或者 c=’.’),所以str 匹配完往前挪一个,pattern 继续匹配,因为可以匹配多个,继续看str[0…n−2]和 pattern[0…m−1]是否匹配。

转移方程
f[i][j] 代表 str 的前 i 个和 pattern 的前 j 个能否匹配
对于前面两个情况,可以合并成一种情况 f[i][j]=f[i−1][j−1]
对于第三种情况,对于 c* 分为看和不看两种情况
不看:直接砍掉正则串pattern 的后面两个, f[i][j]=f[i][j−2]
看:正则串pattern 不动,主串str前移一个进行对比,相等则f[i][j]=f[i−1][j]

初始条件
特判:需要考虑空串空正则
空串和空正则是匹配的,f[0][0]=true
空串和非空正则,不能直接定义 true 和 false,必须要计算出来。(比如str= ‘’ ‘’ ,pattern=“a*b*c*”)
非空串和空正则必不匹配,f[1][0]=…=f[n][0]=false
非空串和非空正则,那肯定是需要计算的了。
大体上可以分为空正则和非空正则两种,空正则也是比较好处理的,对非空正则我们肯定需要计算,非空正则的三种情况,前面两种可以合并到一起讨论,第三种情况是单独一种,那么也就是分为当前位置是 ‘*’ (星)和 非’*’ 两种情况了。
结果
开数组要开 n+1 ,这样对于空串的处理十分方便。结果就是 f[n][m]

时间复杂度O(MN):其中 m 和n 分别是字符串 str 和 pattern 的长度,递归全部遍历
空间复杂度O(MN) 二维数组

代码

import java.util.*;


public class Solution {
    public boolean match (String str, String pattern) {
        if (str.length() == 0 && pattern.length() == 0) {
            return true;
        }
        return matchCore(str, pattern);
        
    }
    public boolean matchCore(String str, String pattern) {
        int s = str.length();
        int p = pattern.length();
        //00 用于存放两个空字符串的结果 dp[i][j] 表示模式串前j个是否与字符串前i个匹配
        boolean[][] res = new boolean[s+1][p+1];
        for (int i=0; i<=s; i++) {
            for (int j=0; j<=p; j++) {
                //分成空正则和非空正则两种
                if (j == 0) {
                    //只有字符串和正则串都为空的时候才匹配 返回true
                    //当正则串为空,字符串不为空则 返回false
                    res[i][j] = i == 0 ? true : false;
                } else {
                    //非空正则分为两种情况 * 和 非* 
                    //j-1和i-1是因为数组的第1位代表字符串第0位,依次类推
                    if (pattern.charAt(j-1) != '*') {
                        if (i>0 && (str.charAt(i-1)==pattern.charAt(j-1)
                                 || pattern.charAt(j-1)=='.')){
                            //对应位置相等 或者 正则串对应位置为.
                            res[i][j] = res[i-1][j-1];
                        }
                     } else {
                        //如果第j个是* 那么分两种情况,有一种成立即可
                        //case 1 可以直接忽略*前模式的那个元素(*代表出现0次 比如a* 这两个元素做空字符串)
                        //那么dp[i][j]==true 只需满足 dp[i][j-2]==true即可
                        if(j >= 2){
                            res[i][j] = res[i][j-2];
                        }
                        //case 2 如果dp[i][j-2]不等于true那么要满足第j-1个字符(这个字符也可以为‘.’)与第i个字符匹配即可
                        //下标多减1是因为数组是从1开始记录的
                        if(i>0 && j>=2 && (str.charAt(i-1)==pattern.charAt(j-2)
                               || pattern.charAt(j-2)=='.')){
                            //两个case 有一种符合就行
                            res[i][j] = (res[i][j] || res[i-1][j]);
                        }
                    }
                }
            }
        }
        return res[s][p];
    }
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值