算法描述
动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可 行解。
每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。
若用分治法来解这类问题,则分解得到的子问题数目太多,有些子问题被重复计算了很多次。
如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,节省时间。我们可以用一个表来记录所有已解的子问题的答案。不管该子问题以后是否被用到,只要它被计算过,就将其结果填入表中。
这就是动态规划法的基本思路。具体的动态规划算法多种多样,但它们具有相同的填表格式。
1:题目描述
给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。
'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。
2:测试案例
测试案例1:
输入:
s = “aa”
p = “a”
输出: false
解释: “a” 无法匹配 “aa” 整个字符串。
测试案例2:
输入:
s = “aa”
p = " * "
输出: true
解释: ’ * ’ 可以匹配任意字符串。
测试案例3:
输入:
s = “cb”
p = “?a”
输出: false
解释: ‘?’ 可以匹配 ‘c’, 但第二个 ‘a’ 无法匹配 ‘b’。
测试案例4:
输入:
s = “adceb”
p = " * a * b"
输出: true
解释: 第一个 ’ * ’ 可以匹配空字符串, 第二个 ‘*’ 可以匹配字符串 “dce”.
测试案例5:
输入:
s = “acdcb”
p = “a*c?b”
输出: false
提示:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
3:解题思路
和【LeetCode10-正则表达式匹配】类似,都可以采用动态规划算法进行求解,主要抓住的是字符串(S)的前i个和字符模式§前j个的匹配情况。最后判断字符串和匹配规则是否完全匹配。
1:如果字符串模式§ P[j-1] ==’?’ 表示p[j-1]一定和s[j-1]匹配,只需要另dp[i][j] = dp[i-1]j-1;
2:P[j-1]==p[i-1] 和 P[j-1]==’?’ 一致。
3:P[j-1]== ‘*’ 表示为空或者多个字符串,就可以dp[i][j] = dp[i-1][j]|dp[i][j-1]【dp[i-1][j] 是‘ * ’ 表示使用一个* 匹配S[i-1],dp[i][j-1] 是’ * ’ 为空的情况,也可以完全匹配。】;
最后是边界情况,如果第一个是*,或者一直是* , 也可以对S字符串进行全匹配。
4:代码解析
class Solution {
public:
bool isMatch(string s, string p) {
int row = s.size();
int col = p.size();
vector<vector<int>> dp(row + 1, vector<int>(col + 1, 0));//初始化背包
dp[0][0] = 1;
//边界问题
for (int i = 0; i < col; ++i)
{
if (p[i] == '*')
dp[0][i + 1] = true;
else
break;
};
//状态转移方程
for (int i = 1; i <= row; ++i)
{
for (int j = 1; j <= col; ++j)
{
if (p[j - 1] == '*')
dp[i][j] = dp[i][j - 1] | dp[i - 1][j]; //两个状态转移方程
else if (p[j - 1] == '?' || p[j - 1] == s[i - 1])
dp[i][j] = dp[i - 1][j - 1];//两个状态转移方程
};
};
return dp[row][col];
}
};
5:题目总结
本篇文章也是对动态规划的一个思考,对于未知,且拿到手上不知道的题目,需要刻苦专研,方可百炼成钢。对于动态规划的使用,我现在也不是特别清楚,该如何设计,该如何转移,后面我也将进行大量的刷题,能够使得自己对于动态规划达到一个使用流畅的程度。后面自己的博客将会优化。思路和总结概括,还有对状态背包的一个合理阐述。