正则表达式匹配 - 动规解法

题目

  输入两个字符串 sp,判断两字符串是否匹配。规则如下:

s 仅能包含26个小写字母 a-z或者是空字符串
p 仅能包含26个小写字母和字符 .*
.匹配任何单一字符
*指定与它相邻的前一个字符的出现次数,可以是0次也可以是无数次

样例(先输入的是s,再输入p)

输入:
aa
a*
输出:
true
输入:
mississippi
mis*is*p*
输出:
false

分析

   字符串匹配问题我第一个想到的便是KMP算法,但是KMP算法的匹配是基于字符的逐个比对,不像正则表达式匹配,*涉及到字符串的匹配。而且在一遇到*的时候,我们并不能直接确定这个*代表前一个字符出现几次合适,是要结合前后字符的情况考虑的。因此可以看作是一个动态规划的过程。

状态转移:

设dp(i, j)是一个布尔值,代表p的前i长度的子串是否与s的前j长度的子串相匹配。
显然 dp(0, 0) = true,两个空字符串必定相匹配

	if p的第i个字符与s的第j个字符相同,或者p的第i个字符为·(即p的第i个字符与s的第j个字符相配)
		dp(i, j) = dp(i-1, j-1)
		
	else if p的第i个字符为*
		在这种情况下,如果p的第i-1个字符与s的第j个字符相配(代表p的第i-1个字符的重复次数可以为0,也可以不为0)
			dp(i, j) = dp(i-2, j) || dp(i-2, j-1) || dp(i, j-1)
						 重复0次   || 重复最后一次  || 继续增加重复次数
	    如果p的第i-1个字符与s的第j个字符不相配(那么重复次数只能为0)
	    	dp(i, j) = dp(i-2, j)
	    	
	else
	 	dp(i, j) = false.

代码实现

public class Regular_Expression_Matching {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String s = in.next();
        String p = in.next();
        boolean result = isMatch(s,p);
        System.out.println(result);
    }

    public static boolean isMatch(String s, String p) {
        char[] ss = s.toCharArray();
        char[] pp = p.toCharArray();
        boolean[][] dp = new boolean[pp.length+1][ss.length+1];
        
        //初始化
        dp[0][0] = true; 
        for (int j = 1; j <= ss.length; j++){
            dp[0][j] = false;
        }

        //动规过程
        for (int i = 1; i <= pp.length; i++){
            for (int j = 0; j <= ss.length; j++){
            	//对j = 0的单独处理,防止直接动规造成的 ArrayOutOfBound
                if (j == 0){
                    if (pp[i-1] == '*'){
                        dp[i][0] = dp[i-2][0];
                    }else{
                        dp[i][0] = false;
                    }
                }
                //状态转移
                else {
                    if (pp[i - 1] == ss[j - 1] || pp[i - 1] == '.') {
                        dp[i][j] = dp[i - 1][j - 1];
                    } else if (pp[i - 1] == '*') {
                        if (i - 2 >= 0 && (pp[i - 2] == ss[j - 1] || pp[i - 2] == '.')) {
                            dp[i][j] = dp[i][j - 1] || dp[i - 2][j - 1] || dp[i - 2][j];
                        } else {
                            dp[i][j] = dp[i - 2][j];
                        }
                    } else {
                        dp[i][j] = false;
                    }
                }
            }
        }
        return dp[pp.length][ss.length];
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值