LeetCode-Regular Expression Matching
2017-09-22 15:42
381 查看
算法分析与设计,第三周博客。
Regular Expression Matching
Implement regular expression matching with support for
本题的意思是,给出一个字符串s和另一个字符串p,比较s和p是否匹配。其中p中可能含有通配符'.',点号可以代表任意一个字符,另一个特殊符号是'*',星号代表在此之前出现的符号出现零次或多次。
如果p中不包含点号或者星号,那么很简单,直接比较两个字符串是否一样就好了。然后是如果p中不包含星号,那么这种情况下也很简单,比较这两个字符串的每个字符是否相等,其中 点号可以代表任意字符。最后的情况就是p中含有星号了,重点就是,在这种情况下,又该如何处理呢。
设dp[i][j] = true 如果字符串s从下标i开始的子串与字符串p从下标j开始的子串匹配。那么最后只需要
4000
返回dp[0][0],就能代表字符串s和字符串p的匹配结果。
我们一个字符一个字符的来比较:
if (p[j+1] != '*') dp[i][j] = (p[j] == '.' || p[j] == s[i]) && dp[i+1][j+1];
这个情况还是挺直观的,当p的第j个字符的下一个字符不是星号的时候,此时两个字符的匹配只与当前两个字符串的对应字符有关,只需要比较这两个字符串能否匹配就好了。然后进一步看比较剩下的部分是否匹配,即dp[i+1][j+1]是否为真。
if (p[j+1] == '*') dp[i][j] = dp[i][j+2] || ((p[j]
== '.' || p[j] == s[i]) && dp[i+1][j]);这种情况下稍微比上一种情况复杂,此时的结果由两部分组成, dp[i][j+2] 和(p[j]
== '.' || p[j] == s[i]) && dp[i+1][j]。先来看第二部分,这一个部分和上一种情况还是挺相似的,比较当前两个字符是否匹配,并进一步比较剩下的字符串。这里和情况1的不同点在于,一个字符后面含有星号,就意味着这个字符可能出现多次,现在先假设这个字符已经出现了一个并成功匹配了,因为如果不满足这个假设,那么后面那部分的结果就必然会在p[j]
== '.' || p[j] == s[i]阶段就返回false,从而不需要进行进一步的判断了。所以我们这个假设是有意义和道理的,那么现在当前字符已经出现一次了,我们有理由相信它有可能会再次出现,所以进一步的判断是看dp[i+1][j]的结果。讨论完第二部分,再返回讨论第一部分的。其实结果已经很显然了,在第二部分中,星号的存在使得当前字符至少出现了一次。而星号的存在是让前一个字符出现零次或者多次。多次的结果已经在第二部分讨论过了,那么第一部分就是星号之前的字符并没有出现的情况,也就是,当前字符与它的下一个字符,星号,并没有出现在匹配过程中,也就是字符串p跳过了这两个字符,直接进行下一步的匹配,所以结果就是dp[i][j+2]。有了这两个部分,那么带有星号的情况也被解决了。
所实现的代码如下所示:
因为一开始实现的时候并没有用数组保存状态,而是使用递归调用的方法,所以这个版本写出来是在递归的版本上进行修改得到的,虽然和传统的动态规划的形式不一样,但实际上是一致的。最后来看下这个算法的时间复杂度,申明了一个数组dp,大小为|s|*|p|,当为这个数组的每个位置都赋值时,结果就出来了,而为每一个位置赋值的时间复杂度是O(1),进行了若干次的比较,所以整个算法的时间复杂度和空间复杂度都是O(|s|*|p|)。
Regular Expression Matching
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") → false isMatch("aa","aa") → true isMatch("aaa","aa") → false isMatch("aa", "a*") → true isMatch("aa", ".*") → true isMatch("ab", ".*") → true isMatch("aab", "c*a*b") → true
本题的意思是,给出一个字符串s和另一个字符串p,比较s和p是否匹配。其中p中可能含有通配符'.',点号可以代表任意一个字符,另一个特殊符号是'*',星号代表在此之前出现的符号出现零次或多次。
如果p中不包含点号或者星号,那么很简单,直接比较两个字符串是否一样就好了。然后是如果p中不包含星号,那么这种情况下也很简单,比较这两个字符串的每个字符是否相等,其中 点号可以代表任意字符。最后的情况就是p中含有星号了,重点就是,在这种情况下,又该如何处理呢。
设dp[i][j] = true 如果字符串s从下标i开始的子串与字符串p从下标j开始的子串匹配。那么最后只需要
4000
返回dp[0][0],就能代表字符串s和字符串p的匹配结果。
我们一个字符一个字符的来比较:
if (p[j+1] != '*') dp[i][j] = (p[j] == '.' || p[j] == s[i]) && dp[i+1][j+1];
这个情况还是挺直观的,当p的第j个字符的下一个字符不是星号的时候,此时两个字符的匹配只与当前两个字符串的对应字符有关,只需要比较这两个字符串能否匹配就好了。然后进一步看比较剩下的部分是否匹配,即dp[i+1][j+1]是否为真。
if (p[j+1] == '*') dp[i][j] = dp[i][j+2] || ((p[j]
== '.' || p[j] == s[i]) && dp[i+1][j]);这种情况下稍微比上一种情况复杂,此时的结果由两部分组成, dp[i][j+2] 和(p[j]
== '.' || p[j] == s[i]) && dp[i+1][j]。先来看第二部分,这一个部分和上一种情况还是挺相似的,比较当前两个字符是否匹配,并进一步比较剩下的字符串。这里和情况1的不同点在于,一个字符后面含有星号,就意味着这个字符可能出现多次,现在先假设这个字符已经出现了一个并成功匹配了,因为如果不满足这个假设,那么后面那部分的结果就必然会在p[j]
== '.' || p[j] == s[i]阶段就返回false,从而不需要进行进一步的判断了。所以我们这个假设是有意义和道理的,那么现在当前字符已经出现一次了,我们有理由相信它有可能会再次出现,所以进一步的判断是看dp[i+1][j]的结果。讨论完第二部分,再返回讨论第一部分的。其实结果已经很显然了,在第二部分中,星号的存在使得当前字符至少出现了一次。而星号的存在是让前一个字符出现零次或者多次。多次的结果已经在第二部分讨论过了,那么第一部分就是星号之前的字符并没有出现的情况,也就是,当前字符与它的下一个字符,星号,并没有出现在匹配过程中,也就是字符串p跳过了这两个字符,直接进行下一步的匹配,所以结果就是dp[i][j+2]。有了这两个部分,那么带有星号的情况也被解决了。
所实现的代码如下所示:
boolean match(String s, int i, String p, int j, int[][] dp) { if (j >= p.length()) return i >= s.length(); if (i >= s.length()) { while (j+1 < p.length()) { int k = j; if (p.charAt(j+1) == '*') { j = j+2; } if (k == j) break; } return j >= p.length(); } if (j+1 < p.length()) { if (p.charAt(j+1) == '*') { if (j+2 < p.length()) { if (dp[i][j+2] == 0) { dp[i][j+2] = match(s, i, p, j+2, dp) ? 1 : -1; } boolean unused = dp[i][j+2] == 1; if (unused) return true; } if (p.charAt(j) == '.' || s.charAt(i) == p.charAt(j)) { dp[i][j] = match(s, i+1, p, j, dp) ? 1 : -1; return dp[i][j] == 1; } } } if (p.charAt(j) == '.' || s.charAt(i) == p.charAt(j)) { dp[i][j] = match(s, i+1, p, j+1, dp) ? 1 : -1; return dp[i][j] == 1; } dp[i][j] = -1; return false; } public boolean isMatch(String s, String p) { int[][] dp = new int[s.length()][p.length()]; return match(s, 0, p, 0, dp); }
因为一开始实现的时候并没有用数组保存状态,而是使用递归调用的方法,所以这个版本写出来是在递归的版本上进行修改得到的,虽然和传统的动态规划的形式不一样,但实际上是一致的。最后来看下这个算法的时间复杂度,申明了一个数组dp,大小为|s|*|p|,当为这个数组的每个位置都赋值时,结果就出来了,而为每一个位置赋值的时间复杂度是O(1),进行了若干次的比较,所以整个算法的时间复杂度和空间复杂度都是O(|s|*|p|)。
相关文章推荐
- LeetCode Regular Expression Matching
- leetcode: Regular Expression Matching
- LeetCode 10. Regular Expression Matching
- leetcode解题报告:10 Regular Expression Matching
- LeetCode 10. Regular Expression Matching
- LeetCode——Regular Expression Matching
- Leetcode 10. Regular Expression Matching
- LeetCode | Regular Expression Matching
- leetcode 10 -- Regular Expression Matching
- leetcode_010 Regular Expression Matching
- Regular Expression Matching_LeetCode
- LeetCode 10: Regular Expression Matching
- LeetCode - Regular Expression Matching
- Regular Expression Matching - LeetCode
- [LeetCode]Regular Expression Matching、Wildcard Matching
- Leetcode 10 Regular Expression Matching
- [leetcode] 10.Regular Expression Matching
- LeetCode Regular Expression Matching
- leetcode Regular Expression Matching
- [DP] LeetCode Regular Expression Matching