您的位置:首页 > 其它

LeetCode-Regular Expression Matching

2017-09-22 15:42 381 查看
算法分析与设计,第三周博客。

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|)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: