举个例子,s = “abcbbbb” , t = “abc” ,因为 t 只在 s 的前三个字母中出现了,所以如果我们寻找 t 的子串 “ab” 在 s 中出现次数的时候,从第二个 b 开始都是没有任何意义的,因为后面都没有 c 给你匹配了。
代码
dfs+记忆化搜索(c++)
classSolution { public: intnumDistinct(string s, string t){ int n = s.size(), m = t.size(); if (n < m) return0; vector<vector<int> > dp(n, vector<int>(m, -1)); returndfs(n-1, m-1, s, t, dp); }
intdfs(int i, int j, string& s, string& t, vector<vector<int> >& dp){ if (j < 0) return1; if (i < 0) return0; if (dp[i][j] >= 0) return dp[i][j]; int res = dfs(i-1, j, s, t, dp); if (s[i] == t[j]) res += dfs(i-1, j-1, s, t, dp); return dp[i][j]=res; } };
动态规划(c++)
classSolution { public: intnumDistinct(string s, string t){ int n = s.size(), m = t.size(); if (n < m) return0; vector<vector<long> > dp(n, vector<long>(m, 0)); dp[0][0] = (s[0] == t[0]); for (int i = 1; i < n; ++i) { dp[i][0] = dp[i-1][0] + (s[i] == t[0]); } for (int i = 1; i < n; ++i) { for (int j = 1; j < m; ++j) { dp[i][j] = dp[i-1][j]; if (s[i] == t[j]) { dp[i][j] += dp[i-1][j-1]; } } } return dp[n-1][m-1]; } };
动态规划+空间优化(c++)
classSolution { public: intnumDistinct(string s, string t){ int n = s.size(), m = t.size(); if (n < m) return0; vector<long> dp(m, 0); dp[0] = (s[0] == t[0]); for (int i = 1; i < n; ++i) { for (int j = m-1; j >= 1; --j) { if (s[i] == t[j]) { dp[j] += dp[j-1]; } } if (s[i] == t[0]) dp[0]++; } return dp[m-1]; } };
动态规划+空间优化+时间优化(c++)
classSolution { public: intnumDistinct(string s, string t){ int n = s.size(), m = t.size(); if (n < m) return0; vector<long> dp(m, 0); vector<vector<int> > pos(300); for (int j = 1; j < m; ++j) { pos[t[j]].push_back(j); } dp[0] = (s[0] == t[0]); for (int i = 1; i < n; ++i) { int sz = pos[s[i]].size(); for (int j = sz-1; j >= 0; --j) { int p = pos[s[i]][j]; dp[p] += dp[p-1]; } if (s[i] == t[0]) dp[0]++; } return dp[m-1]; } };
dfs+记忆化搜索(python)
classSolution: defnumDistinct(self, s: str, t: str) -> int: n, m = len(s), len(t) if n < m: return0 self.dp = [[-1]*m for _ inrange(n)] returnself.dfs(n-1, m-1, s, t)
defdfs(self, i, j, s, t): if j < 0: return1 if i < 0: return0 ifself.dp[i][j] >= 0: returnself.dp[i][j] res = self.dfs(i-1, j, s, t) if s[i] == t[j]: res += self.dfs(i-1, j-1, s, t) self.dp[i][j] = res return res
动态规划(python)
classSolution: defnumDistinct(self, s: str, t: str) -> int: n, m = len(s), len(t) if n < m: return0 dp = [[0]*m for _ inrange(n)] if s[0] == t[0]: dp[0][0] = 1 for i inrange(1, n): dp[i][0] = dp[i-1][0] + (1if s[i] == t[0] else0) for i inrange(1, n): for j inrange(1, m): dp[i][j] = dp[i-1][j] if s[i] == t[j]: dp[i][j] += dp[i-1][j-1] return dp[n-1][m-1]
动态规划+空间优化(python)
classSolution: defnumDistinct(self, s: str, t: str) -> int: n, m = len(s), len(t) if n < m: return0 dp = [0] * m if s[0] == t[0]: dp[0] = 1 for i inrange(1, n): for j inrange(m-1, 0, -1): if s[i] == t[j]: dp[j] += dp[j-1] if s[i] == t[0]: dp[0] += 1 return dp[m-1]
动态规划+空间优化+时间优化(python)
classSolution: defnumDistinct(self, s: str, t: str) -> int: n, m = len(s), len(t) if n < m: return0 dp = [0] * m pos = [[] for _ inrange(300)] for j inrange(1, m): pos[ord(t[j])].append(j) if s[0] == t[0]: dp[0] = 1 for i inrange(1, n): for j in pos[ord(s[i])][::-1]: if s[i] == t[j]: dp[j] += dp[j-1] if s[i] == t[0]: dp[0] += 1 return dp[m-1]
后记
这题还有个坑爹的地方,就是用动态规划写的时候,数组数据类型必须定义成 long 类型,否则会爆 int 范围,但是最终的答案又在 int 范围内。这其实就是因为动态规划计算了很多无用的状态,这些状态里有超出 int 范围的。而 dfs 用 int 就完全没有问题。
本题还是非常不错的,带你一步步从最好写的 dfs ,然后转化成动态规划,然后优化空间,减少数组维度,最后优化时间。优化时间在 c++ 上面提升不明显,但是 python 提升非常大,直接超过了 100% 的方法。
时间上还有一些小 trick ,我这里没有考虑,留给大家思考。比如对于状态 (i, j) ,如果 n-i < m-j ,那么就没必要更新了,因为 s 中剩下来的字符串都不够 t 剩下来的去匹配的。