吐槽:
首先java的空指针异常的确是思路不够清晰导致的,但是做题方面和c++比起来也太坑了吧。搞明白算法就不容易了,居然还要处理各种java需要的细节。难度倍增。我直接吐血身亡。。。
解题思路:
这道题的意思是让我们找出一个字符串s的某一个子串。这个子串满足的条件是包含另一个字符串t的所有字符。
那问题就转成了将字符串t的所有字符统计起来。看看s的哪个子串能包含这个统计。(当然,子串长度要最小)。
此题的解法是双指针做法 + 哈希表:
- 我们先统计出字符串 t 的所有字符出现的个数,这里用哈希表存
- 然后我们创建另一个哈希表,去遍历 s 串,代表着 s 的字符出现的对应次数
- 声明一个变量 cnt , 用来统计s串遍历到当前有多少个合法字符。
- 此时用双指针做法,右指针位置的字符先对应加到哈希表中,比对一下这个字符在两个哈希表的值,如果是 t 串的个数多,那说明这个位置是有用的。cnt 要加一。
- 在比对完当前位置是否有用之后。要将前面没有用的都过滤掉,左指针位置的字符假设为k,如果 t 的哈希表统计的k的个数小于 s 的哈希表统计的k的个数。说明 s 表里面有多余的k,那当前位就可以不要了,左指针右移即可。
- 重复5的过程直到失败,此时不能继续移动了,如果移动根据上面的说法 s 中k的个数必定不够。
- 此时判断是否 cnt 的个数等于 t 的长度,如果等于,说明我们找到一个子串,满足包含 t 的所有字符。
- 用一个字符串res来当答案,如果res为空,直接将上面左指针和右指针之间的字符串更新成res,否则看一下找到的子串长度和res谁长,如果res长,就更新掉。
代码实现:
class Solution {
public String minWindow(String s, String t) {
// 用来存s的字符有多少个
Map<Character, Integer> hs = new HashMap<Character, Integer>();
// 用来存t的字符有多少个
Map<Character, Integer> ht = new HashMap<Character, Integer>();
// 统计一下t的每个字符有多少个
for (int i = 0; i < t.length(); i ++ ) {
Integer num = ht.get(t.charAt(i));
if (num == null) {
ht.put(t.charAt(i), 1);
} else {
ht.put(t.charAt(i), num + 1);
}
}
// 定义答案
String res = "";
// 定义合法字符个数:如果s[?]在t中有并且个数还没有达到我们就说这个字符合法
int cnt = 0;
for (int i = 0, j = 0; i < s.length(); i ++ ) {
// 当前位直接+1
Integer num = hs.get(s.charAt(i));
if (num == null) {
hs.put(s.charAt(i), 1);
} else {
hs.put(s.charAt(i), num + 1);
}
Integer htnum = ht.get(s.charAt(i));
// 当合法时,合法字符个数加一
if (htnum != null && hs.get(s.charAt(i)) <= htnum) cnt ++ ;
// 如果当前位没有用,左指针右移即可
// 此处就是我为什么要吐槽。。。j < i要写不然空指针
// 括号要匹配对,不然优先级出问题还是空指针,我裂开来。。!
while(j < i && (ht.get(s.charAt(j)) == null || (hs.get(s.charAt(j)) != null && hs.get(s.charAt(j)) > ht.get(s.charAt(j))))) {
// 记得个数减一,不然遇到这个字符就一直判断为没用。
hs.put(s.charAt(j), hs.get(s.charAt(j)) - 1);
j ++ ;
}
if (cnt == t.length() && (i - j + 1 < res.length() || res.equals(""))) {
res = s.substring(j, i + 1);
}
}
return res;
}
}