题目:
示例:
1.
输入:
s = “aa”;
p = “a”;
输出:false;
解释:"a"无法匹配"aa"整个字符串
输入:
s = “aa”
p = “a*”
输出:true
解释:因为"* "可以匹配零个或多个前一个元素,故"a* “可以匹配"aa”
输入:
s = “ab”
p = ".* ”
输出: true
解释: ".* " 表示可匹配零个或多个(’*’)任意字符(’.’)
思路- -动态规划- - 自顶向下:
一、首先考虑自顶向下的算法,因为自顶向下算法需要完成一个来回,从前向后进行推导,故当自顶向下算法理解后就很容易理解自底向上算法。
二、在自顶向下算法中,假定使用符号s[i:]表示字符串s中从第i个字符到最后一个字符组成的子串,p[j:]则表示模式串p中,从第j个字符到最后一个字符组成的子串,使用match(i,j)表示s[i:]与p[j:]的匹配情况,如果能匹配,则置为true,否则置为false,此为各个子问题以及子问题的状态。
三、在本问题中,match(i,j)能否匹配,完全取决于p[j+1]是否为“* ”,故由此可得到此次动态规划的状态转移方程。
curMatch = i < s.length() && s[i] == p[j] || p[j] =="."
- p[j+1] != "* ", match(i,j) = curMatch && match(i+1, j+1)
- p[j+1] == "* " , match(i,j) = match(i, j+2) || curMatch && match(i+1, j)
Code- - 自顶向下的动态规划
enum Result {
TRUE, FALSE; //枚举结果
}
class Solution {
Result[][] memo;
public boolean isMatch(String s, String p) {
memo = new Result[s.length()+1][p.length()+1];
return match(0, 0, s, p};
}
public boolean match(int i, int j, String s, String p) {
if (memo[i][j] != null) {
return memo[i][j] == Result.TRUE;
}
boolean ans;
if (j == p.length()) {
//如果pattern已经遍历结束
ans = i == s.length();
} else {
//第一位的判断和原来类似
boolean curMatch = (i < s.length() && (p.charAt(j) == s.charAt(j) || p.charAt(j) == "."));
if (j + 1 < p.length() && p.charAt(j + 1) == "* ") {
ans = match(i, j + 2, s, p) || curMatch && match(i+1, j, s, p);
} else {
ans = curMatch && match(i+1, j+i, s, p);
}
}
//保存临时结果
memo[i][j] = ans ? Result.TRUE : Result.FALSE; //此句仅是对上文代码中的判空结果做一个双重肯定
return ans;
}
}
思路分析- -动态规划自底向上:
- 其实代码的核心还是位于判断j+1处是否为"* " 这个符号,故此处同自顶向下相同。
- 自底向上采取从后往前处理,即在二维数表中是向左向上前进,这样一来,就避免掉了默认值的问题,同时也不需要像上面一样引入一个看起来很奇怪的枚举值。
- 数组边缘值也很容易进行判断
Code- - 动态规划自底向上:
class Solution {
boolean[][] dp = new boolean[s.length()+1][p.length()+1];
dp[s.length()][p.length()] = true;
for(int i = s.length(); i >= 0; i--) { //i无法从s-1开始
for(int j = p.length()-1; j >= 0; j--) { //如果j从p.length()开始的话会产生越界错误
//核心代码保持不变
//此处不用判断是否为空的问题
boolean curMatch = i < s.length() && (s.charAt(i) == p.charAt(j) || p.charAt(j) == "."); //在判断为false后就不会执行后边的操作,所以无须担心i+1会越界
//判断* 号
if (j+1 < p.length() && p.charAt(j+1) == "* ") {
//出现0次
//出现1次或多次
dp[i][j] = dp[i][j+2] || curMatch && dp[i+1][j];
} else {
dp[i][j] = curMatch && dp[i+1][j+1];
}
}
}
//直接返回最终结果即可
return dp[0][0];
}
ps1问题:code2中i无法从s-1开始,因为由一个测试用例无法通过 s= “aa”, p=“a*”
PS2:之后有时间会对动态规划类题算法题作出自己的总结和见解。