长度最小的子数组
算法思路
利用单调性,使用“同向双指针”
1.left = 0,right = 0
2.进窗口
3.判断
出窗口
代码
public class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length, sum = 0, len = Integer.MAX_VALUE;
for (int left = 0, right = 0; right < n; right++) {
sum += nums[right]; // 进窗口
while (sum >= target) {
// 判断
len = Math.min(len, right - left + 1); // 更新结果
sum -= nums[left++]; // 出窗口
}
}
return len == Integer.MAX_VALUE ? 0 : len;
}
}
无重复字符的最长子串
代码
public int lengthOfLongestSubstring(String ss) {
char[] s = ss.toCharArray();
int[] hash = new int[128]; // 用数组模拟哈希表
int left = 0, right = 0, n = ss.length();
int ret = 0;
while (right < n) {
hash[s[right]]++; // 进入窗口
while (hash[s[right]] > 1) {
// 判断
hash[s[left++]]--; // 出窗口
}
ret = Math.max(ret, right - left + 1); // 更新结果
right++; // 让下一个字符进入窗口
}
return ret;
}
最大连续1的个数III
算法原理:
转化:找出最长的子数组,0的个数不超过K个
1.left = 0,right = 0
2.进窗口
3.判断
出窗口
更新结果
代码
class Solution {
public int longestOnes(int[] nums, int k) {
int ret = 0;
for (int left = 0, right = 0, zero = 0; right < nums.length; right++) {
if (nums[right] == 0) zero++; // 进窗口
while (zero > k) //判断
if (nums[left++] == 0) zero--; // 出窗口
ret = Math.max(ret, right - left + 1);
}
return ret;
}
}
将x减到0的最小操作数
算法原理
转化:找出最长的子数组的长度,所有元素的和正好等于sum-x
滑动窗口:
1.left = 0, right = 0
2.进窗口
3.判断
出窗口
更新结果
代码
class Solution {
public int minOperations(int[] nums, int x) {
int sum = 0;
for (int a : nums) {
sum += a;
}
int target = sum - x;
// 处理细节
if (target < 0) return -1;
int ret = -1;
for (int left = 0, right = 0, tmp = 0; right < nums.length; right++) {
tmp += nums[right]; // 进窗口
while (tmp > target) // 判断
tmp -= nums[left++]; // 出窗口
if (tmp == target)
ret = Math.max(ret, right - left + 1);// 更新结果
}
if (ret == -1) return ret;
else return nums.length - ret;
}
}
水果成篮
代码
class Solution {
public int totalFruit(int[] fruits) {
int ret = 0;
int n = fruits.length;
int[] hash = new int[n + 1]; // 统计窗口内水果的种类
for (int left = 0, right = 0,kinds = 0; right < n; right++) {
int in = fruits[right];
if (hash[in] == 0) kinds++; // 维护水果种类
hash[in]++; // 进窗口
while (kinds > 2) {
// 判断
int out = fruits[left];
hash[out]--;// 出窗口
if (hash[out] == 0) kinds--;
left++;
}
// 更新结果
ret = Math.max(ret, right - left + 1);
}
return ret;
}
}
找到字符串中所有字母异位词
代码
class Solution {
public List<Integer> findAnagrams(String ss, String pp) {
List<Integer> ret = new ArrayList<>();
char[] s = ss.toCharArray();
char[] p = pp.toCharArray();
int[] hash1 = new int[26]; // 统计字符串 p中每一个字符出现的个数
for (char ch : p) hash1[ch - 'a']++;
int[] hash2 = new int[26]; // 统计窗口中每个字符出现的个数
int m = p.length;
for (int left = 0, right = 0, count = 0; right < s.length; right++) {
char in = s[right];
if (++hash2[in - 'a'] <= hash1[in - 'a']) count++; // 进窗口 + 维护 count
if (right - left + 1 > m) {
// 判断
char out = s[left++];
if (hash2[out - 'a']-- <= hash1[out - 'a']) count--; // 出窗口 + 维护 count
}
// 更新结果
if (count == m) ret.add(left);
}
return ret;
}
}
串联所有单词的子串
代码
class Solution {
public List<Integer> findSubstring(String s, String[] words) {
List<Integer> ret = new ArrayList<>();
Map<String, Integer> hash1 = new HashMap<>(); // 保存字典中所有单词的频次
for (String str : words) hash1.put(str, hash1.getOrDefault(str, 0) + 1);
int len = words[0].length();// 每个单词的长度
int m = words.length;
for (int i = 0; i < len; i++) {
// 执行次数
Map<String, Integer> hash2 = new HashMap<>(); // 保存窗口内单词的频次
for (int left = i, right = i, count = 0; right + len <= s.length(); right += len) {
// 进窗口 + 维护count
String in = s.substring(right, right + len);
hash2.put(in, hash2.getOrDefault(in, 0) + 1);
if (hash2.get(in) <= hash1.getOrDefault(in, 0)) count++;
//判断
if (right - left + 1 > len * m) {
// 出窗口 + 维护count
String out = s.substring(left, left + len);
if (hash2.get(out) <= hash1.getOrDefault(out, 0)) count--;
hash2.put(out, hash2.get(out) - 1);
left += len;
}
// 更新结果
if (count == m) ret.add(left);
}
}
return ret;
}
}
最小覆盖子串
代码
class Solution {
public String minWindow(String ss, String tt) {
char[] s = ss.toCharArray();
char[] t = tt.toCharArray();
int[] hash1 = new int[128]; // 统计字符串t中字符的频次
int kinds = 0; // 字符串t中,又多少种字符
for (char ch : t)
if (hash1[ch]++ == 0) kinds++;
int[] hash2 = new int[128]; // 统计窗口中字符的频次
int minLen = Integer.MAX_VALUE, begin = -1;
for (int left = 0, right = 0, count = 0; right < s.length; right++) {
char in = s[right];
if (++hash2[in] == hash1[in]) count++; // 进窗口 + 维护 count
while (kinds == count) {
// 判断
// 更新结果
if (right - left + 1 < minLen) {
begin = left;
minLen = right - left + 1;
}
char out = s[left++];
if (hash2[out]-- == hash1[out]) count--; // 出窗口 + 维护 count
}
}
if (begin == -1) return new String();
else return ss.substring(begin, begin + minLen);
}
}