设s的长度为n ,pp 的长度为 m ;将 s 的第 i 个字符记为 s_i,p 的第 j个字符记为 p_j ,将 s 的前 i 个字符组成的子字符串记为s[:i] , 同理将 p 的前 j 个字符组成的子字符串记为 p[:j]p[:j] 。
因此,本题可转化为求s[:n] 是否能和p[:m] 匹配。
总体思路是从 s[:1]和 p[:1]是否能匹配开始判断,每轮添加一个字符并判断是否能匹配,直至添加完整个字符串s 和p 。展开来看,假设 s[:i]与 p[:j]可以匹配,那么下一状态有两种:
本题的状态共有 m \times nm×n 种,应定义状态矩阵 dpdp ,dp[i][j]代表 s[:i]与 p[:j]是否可以匹配。做好状态定义,接下来就是根据 「普通字符」 , 「.」 , 「*」三种字符的功能定义,分析出动态规划的转移方程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ppcWmD6q-1678256498154)(image/2021-03-18-20-39-26.png)]
class Solution {
public:bool isMatch(string s, string p) {int m = s.size() + 1, n = p.size() + 1;vector> dp(m, vector(n, false));dp[0][0] = true;for(int j = 2; j < n; j += 2)dp[0][j] = dp[0][j - 2] && p[j - 1] == '*';for(int i = 1; i < m; i++) {for(int j = 1; j < n; j++) {dp[i][j] = p[j - 1] == '*' ?dp[i][j - 2] || dp[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.'):dp[i - 1][j - 1] && (p[j - 1] == '.' || s[i - 1] == p[j - 1]);}}return dp[m - 1][n - 1];}
};
bool isMatch(string s, string p) {return isMatchR(s,p,0,0);}// 递归和循环混搭,果然很恶心。直接使用递归好了。// 递归的条件判断。正则表达式。// 算法设计的关键是,选择一个好的算法技术(递归或循环,两个是冗余的。)// 然后设计好的算法路程。关键在于分类讨论的方式。如何合并类别。bool isMatchR(string &s,string &p,int i,int j){//递归终止的条件if(i>=s.size() && j>=p.size()){return true;}// cout<// 匹配零次 || 匹配一次return isMatchR(s,p,i,j+2) || ((ireturn isMatchR(s,p,i+1,j+1);}if(ireturn isMatchR(s,p,i+1,j+1);}// 不匹配return false;}