10. Regular Expression Matching

一 题目

Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'.

'.' Matches any single character.
'*' Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

Note:

  • s could be empty and contains only lowercase letters a-z.
  • p could be empty and contains only lowercase letters a-z, and characters like . or *.

Example 1:

Input:
s = "aa"
p = "a"
Output: false
Explanation: "a" does not match the entire string "aa".

Example 2:

Input:
s = "aa"
p = "a*"
Output: true
Explanation: '*' means zero or more of the preceding element, 'a'. Therefore, by repeating 'a' once, it becomes "aa".

Example 3:

Input:
s = "ab"
p = ".*"
Output: true
Explanation: ".*" means "zero or more (*) of any character (.)".

Example 4:

Input:
s = "aab"
p = "c*a*b"
Output: true
Explanation: c can be repeated 0 times, a can be repeated 1 time. Therefore, it matches "aab".

Example 5:

Input:
s = "mississippi"
p = "mis*is*p*."
Output: false

二 分析

  正则表达式的匹配。HARD难度

2.1 递归

      我最开始就是想到了递归,就是针对P的不同情况去处理,但是调不出来。。。。卡在了对于*的处理上,因为*太灵活,可以有0个或者多个。从前往后匹配,有个问题就是,遇到每个字符后要考虑后面有没有“*”,还得考虑别越界。做题让你打回原形的就是,这个感觉有个思路,就是死活调不出来,真实自己太菜了。后来看了网上大神的文章。是通过对s和p串进行删减来进行比价的,每次比较时只局限在0,1下标上。这里关键点在于p[1],分别判断是不是‘*’处理。

1. p[1] != ‘*’,就是匹配第[0]位,看p[0]是不是==s[0],或者p[0]=='.'.

2.对于p[1]==‘*’,就是分两种情况分析:

条件是若s不为空且首字符匹配(包括 p[0] ==‘.’) 匹配p[2]之后的字符串(对应的case是例4要考虑到当前p的前两个字符可能匹配不到任何的s).满足直接返回true.不满足则s截除首字母(因为已经匹配过了)继续进行循环。

 返回调用递归函数匹配s和去掉前两个字符的p的结果(前两位已经匹配完成)。

//递归
	public static boolean isMatch(String s, String p) {
		//p 长度为0
		if(p.isEmpty()){
			return s.isEmpty();
		}else if(p.length()==1){
			return  s.length() == 1&&(s.equals(p)||p.equals(".") );
		}else 	if(p.charAt(1)!='*'){
			return !s.isEmpty()&&(s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')&&(isMatch(s.substring(1),p.substring(1))) ;
		}else{
			 //p[1]=='*'
			//首元素匹配
			 while (!s.isEmpty() && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.')) {
                //先删去p的前两个元素检验(当前p的前两个字符可能匹配不到任何的s情况)
				 if (isMatch(s, p.substring(2, p.length()))) {
                    return true;
                }
				//前面的while条件逐一判断s[0]与p[0]是否相等
                s = s.substring(1, s.length());
            }
			//说明p的前两个字符已经匹配完毕,p取后面的字符 
		    return isMatch(s,p.substring(2));
		}

    }

Runtime: 94 ms, faster than 5.02% of Java online submissions for Regular Expression Matching.

Memory Usage: 44 MB, less than 9.09% of Java online submissions forRegular Expression Matching.

显然太慢了。

2.2 动态规划

之前在看最长子串的时候,就是动态规划。

到这个问题上,怎么传递状态又卡住了,这也是我比较虚的一点,觉得看明白了,写代码调不出来。

p[j-1]是正常字符串中的字符位置,因为动态规划多加了一个空的占位符。

看了老印的视频:https://www.youtube.com/watch?v=l3hda49XcDE

可以自己动手画图来理解。

//动态规划
	public static boolean isMatch(String s, String p) {
	    if (s == null || p == null) {
            return false;
        }
        int m = s.length(), n = p.length();
        boolean[][] dp = new boolean[m + 1][n + 1];
        dp[0][0] = true;
	    for (int i = 1; i < n; i++) { // 初始化第一行,p匹配s = ""
            if (p.charAt(i) == '*' && dp[0][i - 1]) {
                dp[0][i + 1] = true;
            }
	    }
	    for(int i=1;i<=m;i++){
	    	for(int j=1;j<=n;j++){
	    		// p[j - 1]不是"*"的情况,单字符匹配
	    		 if(s.charAt(i-1) == p.charAt(j-1) || p.charAt(j-1) == '.'){
	    			       dp[i][j] = dp[i-1][j-1];
	    		 }
	    		 else  if (p.charAt(j - 1) == '*') {
	    			  //*前面字符匹配0次
                     if (p.charAt(j - 2) != s.charAt(i - 1) && p.charAt(j - 2) != '.') {
                         dp[i][j] = dp[i][j - 2];
                     } else {
                
                    	 	/*	
                    		 *       dp[i][j] = dp[i-1][j] // in this case, a* counts as multiple a 
                    		 *       dp[i][j] = dp[i][j-1] // in this case, a* counts as single a 
                    		 *       dp[i][j] = dp[i][j-2] // in this case, a* counts as empty
                    		 */
                         dp[i][j] = dp[i][j - 2] || dp[i][j - 1] || dp[i - 1][j];
                     }		                 
	    			 
	    		 }	    		
	    	}
	    	
	    }
        
	    return dp[m][n];
	}

如果对于p.charat[j-1]=='*" 觉得别扭,可以这样换个写法:

                         dp[i][j] = dp[i][j - 2];
                         if (p.charAt(j - 2) == s.charAt(i-1) || p.charAt(j - 2) == '.'){                
                           dp[i][j] =  dp[i][j - 2] || dp[i][j - 1] || dp[i - 1][j];
                        }   

下面的不能去掉dp[i][j - 2] ||,

不然这种case过不了:

"aasdfasdfasdfasdfas"
"aasdf.*asdf.*asdf.*asdf.*s"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值