思路
不加备忘录的暴力思路
主要是* 星号, 据题意 星号 肯定是在某个字符的后面,时刻记住动态规划的主旨就是划分成子问题,那我们就一个一个去比较,当发现有*号时,就将 星号 与其前一个字母看作一个子问题, (并铭记关注当下),当下对于 星号 的处理就是两种情况,要么作用0次,那就将这个size为2的子串直接跳过去p = p.substr(2)
, 要么当下
作用1次,与当下的s作比较s[0] == p[0]
,并s往后移, p是不动的,还要靠 星号 前面的正主来比较的
那么公式就是isMatch(s, p.substr(2)) || (当前是否相等 && isMatch(s.substr(1), p))
关于当前是否相等,就用到了’.’,点号,点号可以代替任意一个字符,所以first_place = (!s.empty() && (s[0] == p[0] ||p[0] == '.'))
综上isMatch(s, p.substr(2)) || (first_place && isMatch(s.substr(1), p))
小细节上的考量:在测试案例上有一个是aa和a*, 最后s走完了,p还有两个,所以我们在长度判断上要始终以p去判断s
加备忘录
加备忘录就是为了避免重复计算,这道题的重复计算会出现在什么情况呢,,,没想出来
那,可以看看是否会有子问题重叠的情况呢,从状态入手,假设i, j分别是当前s, p作用点的位置,那么根据上述思路,有下面几种状态,初始dp[i][j], 星号作用0次直接匹配串跳过两个dp[i][j+2], 星号作用1次s串往前走1个dp[i+1][j], 都往前走的dp[i+1][j+1]
那么判断是否有子问题重叠的关键就是看dp[i][j]到dp[i+2][j+2]
是否超过一种路径可达,很明显在本题中有超过两种路径,所以有子问题
那,就用i、j存入备忘录避免重复计算吧(觉得画个图可能就立马能知道重复的情况,でも ま——)
题目
代码(不加备忘录
class Solution {
public:
bool isMatch(string s, string p) {
if(p.empty())
return s.empty() ;
bool first_place = (!s.empty() && (s[0] == p[0] ||p[0] == '.'));
if(p.size() >1 && p[1] == '*')
{//关注当下,作用0次直接跳过p的两个 || (先)作用1次,s往后走1,s还是要用那个*前面的元素的
return (isMatch(s, p.substr(2)) || (first_place && isMatch(s.substr(1), p)));
}
else{
return first_place && isMatch(s.substr(1), p.substr(1));
}
}
};
//aa a* 怎么判断呢 最后还有a 和a*的时候,正常走if,直到s空,p只有*,
//nonono p不是只有*了,作用1次,p是不往后走的,p还是a*, 所有还是走if上,此刻p也就空了,到第一个if
//不要以s去判断,以p的长度去判断,
加备忘录代码
class Solution {
public:
bool dp(string &s, int i, string &p, int j, vector<vector<int>> &memo) {
if (memo[i][j] != -1)
return memo[i][j];
if (j == p.length())
memo[i][j] = (i == s.length());
else if (j + 1 < p.length() && p[j + 1] == '*')
memo[i][j] = dp(s, i, p, j + 2, memo) || i < s.length() && (s[i] == p[j] || p[j] == '.') && dp(s, i + 1, p, j, memo);
else
memo[i][j] = i < s.length() && (s[i] == p[j] || p[j] == '.') && dp(s, i + 1, p, j + 1, memo);
return memo[i][j];
}
bool isMatch(string s, string p) {
vector<vector<int>> memo(s.length() + 1, vector<int>(p.length() + 1, -1));
return dp(s, 0, p, 0, memo);
}
};