1.最长区间
模板:
int maxLength(int[] A) {
int N = A.length;
// 区间的左指针
int left = -1;
int ans = 0;
for (int i = 0; i < N; i++) {
// assert 在加入A[i]之前,(left, i-1]是一个合法有效的区间
// step 1. 直接将A[i]加到区间中,形成(left, i]
// step 2. 将A[i]加入之后,惰性原则
while (check((left, i]))/*TODO 检查区间状态是否满足条件*/) {
++left; // 如果不满足条件,移动左指针
// TODO 修改区间的状态
}
// assert 此时(left, i]必然满足条件
ans = max(ans, i - left);
}
return ans; // 返回最优解
}
class Solution {
public int lengthOfLongestSubstring(String s) {
final int N = (null == s ? 0 : s.length());
final int[] pos = new int[256];
Arrays.fill(pos, -1);
int res = 0;
int left = -1;
for (int i = 0; i < N; i ++) {
final int idx = (int)s.charAt(i);
while (pos[idx] > left) {
left = pos[idx];
}
pos[idx] = i;
res = Math.max(res, i - left);
}
return res;
}
}
class Solution {
public int characterReplacement(String s, int k) {
final int N = s.length();
int left = -1;
int res = 0;
int oneNumber = 0;
int[] cnt = new int[256];
for (int i = 0; i < N; i ++) {
final int c = s.charAt(i);
cnt[c] ++;
oneNumber = Math.max(oneNumber, cnt[c]);
while (i - left - oneNumber > k) {
final int ct = s.charAt(++ left);
cnt[ct] --;
}
res = Math.max(res, i - left);
}
return res;
}
}
class Solution {
// 根据题目设计数据结构
private class Counter extends HashMap<Integer, Integer> {
public Integer get(final Integer key) {
return super.containsKey(key) ? super.get(key) : 0;
}
public void add(final Integer key, final Integer value) {
super.put(key, this.get(key) + value);
}
}
public int totalFruit(int[] tree) {
final int n = tree.length;
int left = -1;
int res = 0;
final Counter counter = new Counter();
for (int i = 0; i < n; i ++) {
final Integer rightType = tree[i];
counter.add(rightType, 1);
while (counter.size() > 2) {
final Integer leftType = tree[++ left];
counter.add(leftType, -1);
if (0 == counter.get(leftType)) {
counter.remove(leftType);
}
}
res = Math.max(res, i - left);
}
return res;
}
}
参考题解:
https://github.com/lagoueduCol/Algorithm-Dryad/blob/main/10.DoublePointer/904.%E6%B0%B4%E6%9E%9C%E6%88%90%E7%AF%AE.java?fileGuid=xxQTRXtVcqtHK6j8
public class Solution {
private class Counter extends HashMap<Character, Integer> {
public Integer get(Character key) {
return super.containsKey(key) ? super.get(key) : 0;
}
public void add(Character key, Integer value) {
super.put(key, this.get(key) + value);
if (0 == this.get(key)) {
super.remove(key);
}
}
}
/**
* @param s: A string
* @param k: An integer
* @return: An integer
*/
public int lengthOfLongestSubstringKDistinct(String s, int k) {
// write your code here
final int n = s.length();
int left = -1;
int res = 0;
final Counter counter = new Counter();
for (int i = 0; i < n; i ++) {
final Character rightCh = (Character)s.charAt(i);
counter.add(rightCh, 1);
while (counter.size() > k) {
final Character leftCh = (Character)s.charAt(++ left);
counter.add(leftCh, -1);
}
res = Math.max(res, i - left);
}
return res;
}
}
class Solution {
public int longestMountain(int[] arr) {
int n = arr.length;
int res = 0;
for (int i = 1; i < n - 1; i ++) {
int left = i - 1;
while (left >= 0) {
if (arr[left] < arr[left + 1]) {
left --;
} else {
break;
}
}
if (left + 1 == i) {
continue;
}
int right = i + 1;
while (right < n) {
if (arr[right - 1] > arr[right]) {
right ++;
} else {
break;
}
}
if (right == i + 1) {
continue;
}
res = Math.max(res, right - left - 1);
}
return res;
}
}
// myself pass
2.区间计数
模板:
int rangeCounter(int[] A) {
int N = A.length;
// 区间的左指针
int left = -1;
int ans = 0;
// 不变式0: 最开始的区间为(-1, -1] 是一个空区间
// 我们认为空区间总是满足条件!
for (int i = 0; i < N; i++) {
// 不变式1: 在加入A[i]之前,(left, i-1]是一个合法有效的区间
// step 1. 直接将A[i]加到区间中,形成(left, i]
// step 2. 将A[i]加入之后,惰性原则
while (check((left, i]))/*TODO 检查区间状态是否满足条件*/) {
++left; // 如果不满足条件,移动左指针
// TODO 修改区间的状态
}
// 不变式2:此时(left, i]必然合法
// 累计区间个数
ans += i - left;
}
return ans; // 返回最优解
}
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
final int n = nums.length;
int left = -1;
int s = 1;
int res = 0;
for (int i = 0; i < n; i ++) {
s *= nums[i];
while (left < i && s >= k) {
s /= nums[++ left];
}
res += (i - left);
}
return res;
}
}
public class Solution {
/**
* @param nums: an array
* @param k: a target value
* @return: the maximum length of a subarray that sums to k
*/
public int maxSubArrayLen(int[] A, int k) {
final int N = A == null ? 0 : A.length;
Map<Long, Integer> H = new HashMap<>();
Long s = Long.valueOf(0);
int ans = 0;
H.put(s, -1);
for (int i = 0; i < N; i++) {
s += A[i];
Long want = Long.valueOf(s - k);
int wantPos = H.getOrDefault(want, N + 1);
ans = Math.max(ans, i - wantPos);
// 如果已经存在了
if (!H.containsKey(s)) {
H.put(s, i);
}
}
return ans;
}
}
3.定长区间
定长区间问题是要找到一个固定长度的区间,并且这个区间必须满足某种条件。所以求解定长区间问题,实质上是需要找满足两个条件的子串。
子串的长度固定。由于长度固定,因此,定长区间问题不需要满足单调性。
子串必须满足某种条件。
class Solution {
public boolean checkInclusion(String s1, String s2) {
final int aLen = (null == s1 ? 0 : s1.length());
final int bLen = (null == s2 ? 0 : s2.length());
int[] aStr = new int[256];
for (int i = 0; i < aLen; i ++) {
aStr[s1.charAt(i)] ++;
}
int[] bStr = new int[256];
int left = -1;
for (int i = 0; i < bLen; i ++) {
bStr[s2.charAt(i)] ++;
if (i - left < aLen) {
continue;
}
boolean same = true;
for (int j = 0; j < 256 && same; j ++) {
same = (aStr[j] == bStr[j]);
}
if (same) {
return true;
}
left ++;
bStr[s2.charAt(left)] --;
}
return false;
}
}
class Solution {
private class Counter extends HashMap<String, Integer> {
public Integer get(String key) {
return super.containsKey(key) ? super.get(key) : 0;
}
public void add(String key, Integer value) {
if ("".equals(key)) {
return;
}
super.put(key, this.get(key) + value);
if (0 == this.get(key)) {
super.remove(key);
}
}
}
public List<Integer> findSubstring(String s, String[] words) {
final int wordsLen = words.length;
Counter wordsLenCounter = new Counter();
for (int i = 0; i < wordsLen; i ++) {
wordsLenCounter.add(words[i], 1);
}
final int wordsN = words[0].length();
final int wordsNumber = words.length;
final int sLen = s.length();
int left = -1;
List<Integer> res = new ArrayList<>();
for (int i = 0; i < sLen; i ++) {
if (i - left < (wordsN * wordsLen)) {
continue;
}
Counter sCounter = new Counter();
for (int j = left + 1; j <= i - wordsN + 1; j += wordsN) {
String str = s.substring(j, j + wordsN);
sCounter.add(str, 1);
}
left ++;
boolean same = wordsLenCounter.size() == sCounter.size();
if (same) {
for (String sw : wordsLenCounter.keySet()) {
if (!(wordsLenCounter.get(sw).equals(sCounter.get(sw)))) {
same = false;
break;
}
}
}
if (same) {
res.add(left);
}
}
return res;
}
}
//
class Solution {
private class Counter extends HashMap<Character, Integer> {
public Integer get(Character key) {
return super.containsKey(key) ? super.get(key) : 0;
}
public void add(Character key, Integer value) {
super.put(key, this.get(key) + value);
if (0 == super.get(key)) {
super.remove(key);
}
}
}
public String minWindow(String s, String t) {
final int tLen = t.length();
final Counter tCounter = new Counter();
for (int i = 0; i < tLen; i ++) {
Character key = (Character)t.charAt(i);
tCounter.add(key, 1);
}
final int sLen = s.length();
int left = -1;
int minLen = sLen + 1;
final Counter sCounter = new Counter();
int resL = 0;
int resR = 0;
for (int i = 0; i < sLen; i ++) {
Character key = (Character)s.charAt(i);
sCounter.add(key, 1);
if (i - left < tLen) {
continue;
}
boolean same = true;
while (same) {
for (Character c : tCounter.keySet()) {
if (tCounter.get(c) > (sCounter.get(c))) {
same = false;
break;
}
}
if (same) {
if (minLen > i - left) {
resL = left + 1;
resR = i + 1;
minLen = i - left;
}
left ++;
sCounter.add((Character)s.charAt(left), -1);
}
}
}
return s.substring(resL, resR);
}
}
class Solution {
public int minSubArrayLen(int s, int[] nums) {
final int n = nums.length;
int left = -1;
int res = 0;
for (int i = 0; i < n; i ++) {
res += nums[i];
}
if (res < s) {
return 0;
}
res = 0;
int minLen = n;
for (int i = 0; i < n; i ++) {
res += nums[i];
while (res >= s) {
minLen = Math.min(minLen, i - left);
left ++;
res -= nums[left];
}
}
return minLen;
}
}
训练
class Solution {
public double findMaxAverage(int[] nums, int k) {
final int n = nums.length;
int left = -1;
double sum = 0;
double res = Integer.MIN_VALUE;
for (int i = 0; i < n; i ++) {
sum += nums[i];
if (i - left < k) {
continue;
}
res = Math.max(res, sum / k);
left ++;
sum -= nums[left];
}
return res;
}
}
// https://kaiwu.lagou.com/course/courseInfo.htm?courseId=685#/detail/pc?id=6699
class Solution {
public int maxSatisfied(int[] customers, int[] grumpy, int minutes) {
final int n = customers.length;
int sum = 0;
int res = Integer.MIN_VALUE;
for (int i = 0; i < n; i ++) {
if (0 == grumpy[i]) {
sum += customers[i];
}
}
int left = -1;
for (int i = 0; i < n; i ++) {
if (1 == grumpy[i]) {
sum += customers[i];
}
if (i - left < minutes) {
continue;
}
res = Math.max(res, sum);
left ++;
if (1 == grumpy[left]) {
sum -= customers[left];
}
}
return res;
}
}
// https://kaiwu.lagou.com/course/courseInfo.htm?courseId=685#/detail/pc?id=6699
class Solution {
public int maxScore(int[] cardPoints, int k) {
final int n = cardPoints.length;
final int windowLen = n - k;
int sum = 0;
for (int i = 0; i < n; i ++) {
sum += cardPoints[i];
}
if (0 == windowLen) {
return sum;
}
int sumWindow = 0;
int minValue = sum;
int left = -1;
for (int i = 0; i < n; i ++) {
sumWindow += cardPoints[i];
if (i - left < windowLen) {
continue;
}
minValue = Math.min(minValue, sumWindow);
left ++;
sumWindow -= cardPoints[left];
}
return sum - minValue;
}
}
//
class Solution {
private class Counter extends HashMap<Character, Integer> {
private Integer validSum = 0;
public Integer getValidSum() {
return validSum;
}
public Integer get(Character key) {
return super.containsKey(key) ? super.get(key) : 0;
}
public void add(Character key, Integer value) {
super.put(key, this.get(key) + value);
validSum += value;
if (0 == this.get(key)) {
super.remove(key);
}
}
}
private boolean isValid(Character ch) {
if ('a' == (char)ch || 'e' == (char)ch || 'i' == (char)ch || 'o' == (char)ch || 'u' == (char)ch) {
return true;
}
return false;
}
public int maxVowels(String s, int k) {
final int n = s.length();
int left = -1;
int res = 0;
Counter counter = new Counter();
for (int i = 0; i < n; i ++) {
Character ch = s.charAt(i);
if (this.isValid(ch)) {
counter.add(ch, 1);
}
if (i - left < k) {
continue;
}
res = Math.max(res, counter.getValidSum());
left ++;
Character chLeft = s.charAt(left);
if (this.isValid(chLeft)) {
counter.add(chLeft, -1);
}
}
return res;
}
}
class Solution {
public int minOperations(int[] nums, int x) {
final int n = nums.length;
int k = 0;
for (int i = 0; i < n; i ++) {
k += nums[i];
}
k -= x;
if (k < 0) {
return -1;
} else if (k == 0) {
return n;
}
int left = -1;
int sum = 0;
int maxLen = 0;
for (int i = 0; i < n; i ++) {
sum += nums[i];
while (sum > k) {
left ++;
sum -= nums[left];
}
if (sum == k) {
maxLen = Math.max(maxLen, i - left);
}
}
return 0 == maxLen ? -1 : n - maxLen;
}
}
class Solution {
public int maximumUniqueSubarray(int[] nums) {
final int n = nums.length;
int sum = 0;
int left = 0;
Set<Integer> set = new HashSet<>();
int res = 0;
for (int i = 0; i < n; i ++) {
if (!set.contains(nums[i])) {
sum += nums[i];
res = Math.max(res, sum);
set.add(nums[i]);
} else {
while (nums[i] != nums[left]) {
sum -= nums[left];
set.remove(nums[left]);
left ++;
}
left ++;
}
}
return res;
}
}
class Solution {
public List<Integer> findAnagrams(String s, String p) {
final int sLen = s.length();
final int pLen = p.length();
final int[] pArr = new int[256];
for (int i = 0; i < pLen; i ++) {
pArr[p.charAt(i)] ++;
}
int left = -1;
final int[] sArr = new int[256];
final List<Integer> res = new ArrayList<>();
for (int i = 0; i < sLen; i ++) {
sArr[s.charAt(i)] ++;
if (i - left < pLen) {
continue;
}
left ++;
boolean same = true;
for (int j = 0; j < 256; j ++) {
if (pArr[j] != sArr[j]) {
same = false;
}
}
sArr[s.charAt(left)] --;
if (same) {
res.add(left);
}
}
return res;
}
}
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
final int n = nums.length;
int oneNum = 0;
int left = -1;
int res = 0;
for (int i = 0; i < n; i ++) {
if (1 == nums[i]) {
oneNum ++;
}
while (i - left > 1 + oneNum) {
left ++;
if (1 == nums[left]) {
oneNum --;
}
}
res = Math.max(res, i - left);
}
return res;
}
}
class Solution {
public int longestOnes(int[] nums, int k) {
final int n = nums.length;
int left = -1;
int oneNum = 0;
int res = 0;
for (int i = 0; i < n; i ++) {
if (1 == nums[i]) {
oneNum ++;
}
while (i - left > k + oneNum) {
left ++;
if (1 == nums[left]) {
oneNum --;
}
}
res = Math.max(res, i - left);
}
return res;
}
}
class Solution {
public int longestSubarray(int[] nums) {
final int n = nums.length;
int zeroNum = 0;
int left = -1;
int res = 0;
for (int i = 0; i < n; i ++) {
if (0 == nums[i]) {
zeroNum ++;
}
while (zeroNum > 1) {
left ++;
if (0 == nums[left]) {
zeroNum --;
}
}
res = Math.max(res, i - left - 1);
}
return res;
}
}
class Solution {
public int maxTurbulenceSize(int[] arr) {
return Math.max(oneContion(arr), twoContion(arr));
}
private int oneContion(int[] arr) {
final int n = arr.length;
int left = 0;
int res = 1;
for (int i = 1; i < n; i ++) {
if (0 == i % 2) {
if (arr[i - 1] <= arr[i]) {
left = i;
continue;
}
} else {
if (arr[i] <= arr[i - 1]) {
left = i;
continue;
}
}
res = Math.max(res, i - left + 1);
}
return res;
}
private int twoContion(int[] arr) {
final int n = arr.length;
int left = 0;
int res = 1;
for (int i = 1; i < n; i ++) {
if (0 == i % 2) {
if (arr[i] <= arr[i - 1]) {
left = i;
continue;
}
} else {
if (arr[i - 1] <= arr[i]) {
left = i;
continue;
}
}
res = Math.max(res, i - left + 1);
}
return res;
}
}
class Solution {
private class Counter extends HashMap<Character, Integer> {
public Integer get(Character key) {
return super.containsKey(key) ? super.get(key) : 0;
}
public void add(Character key, Integer value) {
super.put(key, this.get(key) + value);
if (0 == this.get(key)) {
super.remove(key);
}
}
}
public int lengthOfLongestSubstringTwoDistinct(String s) {
final int n = s.length();
int left = -1;
int res = 0;
Counter counter = new Counter();
for (int i = 0; i < n; i ++) {
counter.add(s.charAt(i), 1);
while (counter.size() > 2) {
left ++;
counter.add(s.charAt(left), -1);
}
res = Math.max(res, i - left);
}
return res;
}
}
class Solution {
private class Counter extends HashMap<Character, Integer> {
public Integer get(Character key) {
return super.containsKey(key) ? super.get(key) : 0;
}
public void add(Character key, Integer value) {
super.put(key, this.get(key) + value);
if (0 == this.get(key)) {
super.remove(key);
}
}
}
public int lengthOfLongestSubstringKDistinct(String s, int k) {
final int n = s.length();
int left = -1;
int res = 0;
Counter counter = new Counter();
for (int i = 0; i < n; i ++) {
counter.add(s.charAt(i), 1);
while (counter.size() > k) {
left ++;
counter.add(s.charAt(left), -1);
}
res = Math.max(res, i - left);
}
return res;
}
}
class Solution {
public int numSubarrayBoundedMax(int[] nums, int left, int right) {
return numSubarrayBoundedMax(nums, right) - numSubarrayBoundedMax(nums, left - 1);
}
private int numSubarrayBoundedMax(int[] nums, int k) {
final int n = nums.length;
int left = -1;
int res = 0;
for (int i = 0; i < n; i ++) {
if (nums[i] > k) {
left = i;
continue;
}
res += (i - left);
}
return res;
}
}
// https://leetcode-cn.com/leetbook/read/sliding-window-and-two-pointers/riika5/
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
final int n = nums.length;
int res = 0;
int sum = 1;
int left = -1;
for (int i = 0; i < n; i ++) {
sum *= nums[i];
while (left < i && sum >= k) {
left ++;
sum /= nums[left];
}
res += (i - left);
}
return res;
}
}
// https://kaiwu.lagou.com/course/courseInfo.htm?courseId=685#/content