最短子字符串问题
问题描述
给定一个字符串S,和一个字符串T,在字符串S中找出最短的包含T中所有字符的子字符串。如果找不到则返回空字符串
示列:
输入:S = "ADOBECODEBANC", T = "ABC"
输出:"BANC"
解题思路
对于求子字符串的问题,我们通常可以使用两个指针left和right(初始指向0)来顺序寻找满足条件的子字符串。示列中right依次遍历ADOBE
到C
则满足包含字符ABC
,记录此时[left,right]子字符串长度。left右移一位,right继续遍历使得[left,right]包含字符ABC
,重复操作直到遍历完成,然后通过记录得出最短子字符串。
附上本人代码,使用HashMap来维护判断是否包含T中的字符。由于每次需要遍历一遍HashMap效率比较低。
public class Solution2 {
public String minWindow(String s, String t) {
if (s == null || t == null || s.length() < t.length()) return "";
HashMap<Character, Integer> map = new HashMap<>();
for (int i = 0; i < t.length(); i++) {
map.put(t.charAt(i), 0);
}
int n = s.length();
int right = 0;
int lo = 0, ro = 0;
int minLen = n;
for (int i = 0; i < n; i++) {
while (!match(map)) {
char c1 = s.charAt(right);
if (map.containsKey(c1)) {
map.replace(c1, 1);
}
right++;
}
if (match(map) && minLen > right - i) {
minLen = right - i;
lo = i;
ro = right;
}
char c = s.charAt(i);
if (map.containsKey(c)) {
map.replace(c, 0);
}
}
return ro - lo > 0 ? s.substring(lo, ro) : "";
}
private boolean match(HashMap<Character, Integer> map) {
for (Map.Entry<Character, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
return false;
}
}
return true;
}
}
附上大神给出的代码,通过int[] map = new int[128]
来判定是否包含字符效率高。思路:字符A
对应ASCII码数字为65。用map[65]++,map[65]--
来标记字符A
class Solution {
public String minWindow(String s, String t) {
if (s == null || t == null || s.length() < t.length()) return "";
int[] map = new int[128];
for (char c : t.toCharArray()) {
map[c]++;
}
int m = s.length(), n = t.length();
int minLen = m + 1, left = 0;
int lo = 0, cnt = 0;
for (int hi = 0; hi < m; hi++) {
char c = s.charAt(hi);
map[c]--;
if (map[c] >= 0) cnt++;
while (cnt == n) {
if (minLen > hi - lo + 1) {
minLen = hi - lo + 1;
left = lo;
}
char pre = s.charAt(lo);
map[pre]++;
if (map[pre] > 0) cnt--;
lo++;
}
}
return minLen == m + 1 ? "" : s.substring(left, left + minLen);
}
}
存在的问题
编码问题,int[] map = new int[128]
表示的是英文的128个字符。如果是中文(占2-3个字节)需要扩充数组长度。
网友评论