思路1:
递归肯定会比较慢, 就直接上动态规划了. 要多建立一行和一列, 因为要考虑空字符串的情况.
flags[i][j]表示子串s[0…i - 1]是否匹配p[0…j - 1].
flags[0][0]一定是真, 因为空字符串一定能匹配空字符串.
除了第0项, 剩下的第0列一定都是假, 因为如果p是空字符串, 那么他就只能匹配空字符串而已.
除了第0项, 剩下的第0行要遵循以下三个条件才能是真:
1) 下标要大于1, 意思是子串必须长度大于等于2;
2) 当前字符必须是’*’;
3) 向前数2个, flags[0][j -2], 意思就是”x*”不重复的时候也要能匹配才可以
初始化工作算是结束了, 下面开始填满剩余的矩阵.
flags[i][j]为真要满足以下条件二选一:
1) 当当前字符为”*”时:
“x*”不重复的时候, 也就是flags[i][j - 2]的值为真;
“x*”至少重复一次的时候, 包括p的上一个字符和s的当前字符相同, 或者p的上一个字符为’.’;
和在重复的情况下, s的上一个子串也匹配
2) 当当前字符不为”*”时:
要看s的上一个子串和p的上一个子串是否匹配;
以及s当前字符和p的当前字符是否相等, 或者p当前字符是不是’.’.
bool isMatch(string s, string p) {
int slen = s.length(), plen = p.length();
bool flags[slen + 1][plen + 1];
flags[0][0] = true;
for (int j = 1; j <= plen; j++)
flags[0][j] = j > 1 && p[j - 1] == '*' && flags[0][j - 2];
for (int i = 1; i <= slen; i++)
flags[i][0] = false;
for (int i = 1; i <= slen; i++) {
for (int j = 1; j <= plen; j++) {
if (p[j - 1] == '*')
flags[i][j] = flags[i][j - 2]
|| (p[j - 2] == s[i - 1] || p[j - 2] == '.')
&& flags[i - 1][j];
else
flags[i][j] = flags[i - 1][j - 1] &&
(p[i - 1] == s[i - 1] || p[i - 1] == '.');
}
}
return flags[slen][plen];
}
第二个版本:
思路依然是动态规划, 但是因为每次我们最多只需要上一次的结果, 所以可以把O(m * n)的空间开销节省为O(n)的, 只开两个数组循环交替存取数据就好, 而不用开辟整个m * n的矩阵空间.
bool isMatch(string s, string p) {
int slen = s.length(), plen = p.length();
int prev = 0, cur = 0;
bool flags[2][plen + 1];
flags[0][0] = true;
for (int j = 1; j <= plen; j++)
flags[0][j] = j > 1 && p[j - 1] == '*' && flags[0][j - 2];
cur = 1 - cur;
for (int i = 1; i <= slen; i++) {
flags[cur][0] = false;
for (int j = 1; j <= plen; j++) {
if (p[j - 1] == '*')
flags[cur][j] = flags[cur][j - 2]
|| (p[j - 2] == s[i - 1] || p[j - 2] == '.')
&& flags[prev][j];
else
flags[cur][j] = flags[prev][j - 1]
&& (p[j - 1] == s[i - 1] || p[j - 1] == '.');
}
swap(cur, prev);
}
return flags[prev][plen];
}