思路一: 不容易啊,自己做出backtracking(dfs)的hard题。哈哈其实我是事先知道了这题要用dfs来做所以才直接往这方向靠的,不然的话也不一定我就能想到dfs。其实朝着dfs方向去想还是很容找到规律的。准确的说这边是使用了recursion and memorization,光用recursion会acception denied,因为time limit exceeded。下面简单说一下自己的思路吧:base case有两个,一个是当s长度小于t长度,返回0;另一个是当t的长度为1,只需简单计算此时s中有几个t就行,返回这个数字。为什么t长度会为1,因为在整个recursion过程中s,t的长度是一直在缩短的。说完了base case,我们来说normal case。我们遍历s,找到所有与t的第一个字符相等的字符,如果找到了,就执行numDistinc(s.substring(i+1),t.substring(1))。这样做的原因是:你想,我们已经在s的i位置找到了与t第一个字符相等的字符,那我们接下来要做什么呢?此时t的第一个字符已经匹配成功,那么我们是不是只要在(s从i之后的字符串)里找有多少个与(t从1之后的字符串)相等的字符串就行了,对吗。你看,这样recursion不就出现了吗,这就是为什么我们要计算numDistinc(s.substring(i+1),t.substring(1))的原因了。最后还有一个要注意的就是,每次recursion的结果我们都存进hashmap中,减少重复计算。思路还是很简单的,下面直接上代码:
class Solution {
private Map<String,Integer> mappings;
private int res(String s, String t){
// 这行代码其实并没有给mappings初始化,传入到numDistinc()的时候mappings = null
// 这行代码可以删除,并不影响结果
mappings = new HashMap<String,Integer>();
return this.numDistinct(s,t);
}
public int numDistinct(String s, String t) {
String key = s + " " + t;
// 这行判断代码必须要有,因为一开始map是空的,map == null
// 这时候containskey会报空指针的错
// 我们只需要重新实例化一下就好
if(mappings == null){mappings = new HashMap<String,Integer>();}
if(mappings.containsKey(key)){return mappings.get(key);}
if(s.length() < t.length()) {return 0;}
int res = 0;
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == t.charAt(0)){
if(t.length() == 1){
for(int j = 0; j < s.length(); j++){
if(s.charAt(j) == t.charAt(0)){
res++;
}
}
return res;
}else{
String s1 = s.substring(i+1);
String t1 = t.substring(1);
res += numDistinct(s1,t1);
}
}
}
mappings.put(key,res);
return res;
}
}
思路二: 可以去lc的解答一看思路,虽然写的文章很长,但是耐心看下去真的很详细,解释的特别好,很有水平。他也是用recursion+memorization来实现的,只是在recursion上的想法和我不同。代码也很有水平,这边我直接上代码了:
class Solution {
// Dictionary that we will use for memoization
private HashMap<Pair<Integer, Integer>, Integer> memo;
private int recurse(String s, String t, int i, int j) {
int M = s.length();
int N = t.length();
// Base case
if (i == M || j == N || M - i < N - j) {
return j == t.length() ? 1 : 0;
}
Pair<Integer, Integer> key = new Pair<Integer, Integer>(i, j);
// Check to see if the result for this recursive
// call is already cached
if (this.memo.containsKey(key)) {
return this.memo.get(key);
}
// Always calculate this result since it's
// required for both the cases
int ans = this.recurse(s, t, i + 1, j);
// If the characters match, then we make another
// recursion call and add the result to "ans"
if (s.charAt(i) == t.charAt(j)) {
ans += this.recurse(s, t, i + 1, j + 1);
}
// Cache the result
this.memo.put(key, ans);
return ans;
}
public int numDistinct(String s, String t) {
this.memo = new HashMap<Pair<Integer, Integer>, Integer>();
return this.recurse(s, t, 0, 0);
}
}
非常清晰的思路以及代码,非常值得学习!
总结:
- 注意我自己方法里面的那个注释。报空指针的错原因是map = null。这边刚开始的时候map里面什么都没有,是空的,就是null,所以需要初始化一下。这边我还是有疑惑的,我觉得我已经在res()里面初始化过了,为什么map还是等于null呢。希望看完这篇文章有答案的同学能给我解答一下疑惑。Thanks in advance!