[LeetCode
来源:互联网 发布:java中xml文件的作用 编辑:程序博客网 时间:2024/05/29 15:52
1 问题
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).The function prototype should be:bool isMatch(const char *s, const char *p)Some examples:isMatch("aa","a") → falseisMatch("aa","aa") → trueisMatch("aaa","aa") → falseisMatch("aa", "a*") → trueisMatch("aa", ".*") → trueisMatch("ab", ".*") → trueisMatch("aab", "c*a*b") → true
2 分析
2.1 该问题的递归性
如果不考虑"."
和"*"
, 检查两个字符串是否匹配的方法是逐位比较,如果这两个字符串有一个位置上的字符不相等,那么这两个字符串不匹配,如果两个字符串所有位置均对应相等,那么这两个字符串匹配。
接下来考虑存在"."
和"*"
的情况:
"."
匹配任意一个字符,如果待比较的两个字符串某一位置上的字符是"."
, 则认为这两个位置上的字符相等,继续下一个位置的比较。"*"
可以匹配0个或者多个紧邻的前一个字符。即"*"
的存在使该字符串变成了多个可能的字符串。因此,必须将"*"
能够表示的所有情况与另外一个字符串相比较,如果存在一种匹配的情况,那么这两个字符串匹配,反之,这两个字符串不匹配。"*"
存在时,"*"
和其紧邻的前一个字符(设为m
)有如下三种可能的匹配情况:- 空字符串”“
- 单一字符
m
- 多个字符
mm...
在对"*"
表示的所有可能情况,进行搜索、确定是否匹配的过程中,如果一种情况匹配失败,就要回溯,选择下一种可能情况来进行操作。因此,该问题具有递归性。
2.2 递归解法
递归,回溯解决该问题的伪代码如下:
isMatch(String s, String p)
:
- base case:
if p == ""
: //对应情况:模式字符串匹配完毕if s == "": return true
else: return false
if s == ""
: //对应情况:源字符串匹配完毕,模式字符串有剩余if p的长度大于1,且p的第二个字符是*
:return isMatch(s,p[2:end])
else: return false
- 递归过程:
if p的长度大于1 且 p[1] == "*":
//对应情况:str*的匹配if s[0] == p[0] or p[0] == ".":
if isMatch(s[1:end],p[2:end]): return true
//对应情况:将str*视为单个字符if isMatch(s[1:end], p): return true;
//对应情况:将str*视为多个字符
if isMatch(s,p[2, end]): return true;
//对应情况:将str*视为空return false
else if s[0] == p[0] or p[0] == ".":
return isMatch(s[1:end], p[1:end])
else: return false
匹配字符串str*
至少会存在3种子问题,因此, 算法的时间复杂度与模式字符串中"*"
的个数有关系,设其为
2.3 使用动态规划优化代码
- 2.1-2.2节分析得出该问题具有递归性,可以分解为多个子问题求解。下面证明:该问题的子问题是重叠的。
以"a*b*c
为例:
上图中虚线方框内的子问题在递归过程中被重复求解,因此子问题是重叠的。 - 该问题具有最优子结构,如果
s
和p
整个字符串匹配,那么它们任何一个对应子字符串匹配。
通过上述分析,可以得出结论: 动态规划可以用于优化代码。
令dp[i][j]
表示s[0:i],p[0,j]
是否匹配。 dp[i][j]
可以通过2.2节中分析的子问题关系逐步计算出来,计算方法为:
If p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1]; If p.charAt(j) == '.' : dp[i][j] = dp[i-1][j-1]; If p.charAt(j) == '*': if p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] if p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.': dp[i][j] = dp[i-1][j] or dp[i][j] = dp[i][j-1] or dp[i][j] = dp[i][j-2]
经过动态规划优化后算法的时间复杂度为s,p
的长度。
3 代码
3.1 递归解法:
public class Solution { public boolean isMatch(String s, String p) { System.out.println("S: "+s + " P: " + p); //base case if(p.length() == 0){ if(s.length() == 0){ return true; }else{ return false; } } if(s.length() == 0){ if(p.length() > 1 && p.charAt(1) == '*'){ return isMatch(s, p.substring(2)); }else{ System.out.println(s+" "+p); return false; } } //recursive procedure if(p.length() > 1 && p.charAt(1) == '*'){ if( s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'){ if(isMatch(s.substring(1), p.substring(2))){ return true; } if(isMatch(s.substring(1), p)){ return true; } } if(isMatch(s, p.substring(2))){ return true; } return false; }else if(s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'){ return isMatch(s.substring(1), p.substring(1)); }else{ return false; } }}
3.2 dp解法
public class Solution { public boolean isMatch(String s, String p) { if (s == null || p == null) { return false; } boolean[][] dp = new boolean[s.length()+1][p.length()+1]; dp[0][0] = true; for (int i = 0; i < p.length(); i++) { if (p.charAt(i) == '*' && dp[0][i-1]) { dp[0][i+1] = true; } } for (int i = 0 ; i < s.length(); i++) { for (int j = 0; j < p.length(); j++) { if (p.charAt(j) == '.') { dp[i+1][j+1] = dp[i][j]; } if (p.charAt(j) == s.charAt(i)) { dp[i+1][j+1] = dp[i][j]; } if (p.charAt(j) == '*') { if (p.charAt(j-1) != s.charAt(i) && p.charAt(j-1) != '.') { dp[i+1][j+1] = dp[i+1][j-1]; } else { dp[i+1][j+1] = (dp[i+1][j] || dp[i][j+1] || dp[i+1][j-1]); } } } } return dp[s.length()][p.length()];}
[1] dp解法的代码来源于 https://discuss.leetcode.com/topic/40371/easy-dp-java-solution-with-detailed-explanation
0 0
- leetcode
- [leetcode]
- LeetCode
- leetcode
- leetcode
- leetcode:
- leetcode:
- LeetCode
- leetcode
- LEETCODE
- leetcode
- leetCode
- leetcode
- [leetcode]
- LeetCode
- leetcode
- leetcode:
- leetcode
- openstack heat部署
- Hibernate: user is not mapped
- PHP表单处理与文件保存
- Unity3D中[SerializeField]特性的使用
- js与form交互方式总结
- [LeetCode
- 复杂度为nlgn的求幂算法
- 指针
- 惠普暗影2 pro ubuntu16.04安装nvidia显卡驱动
- Android 自定义自由选择时间区间的日历控件
- impress.js 源码分析
- 正则表达式:Pattern类与Matcher类详解
- 通过流实现文件读写的方法(上)
- C程序绘制余弦曲线和直线