2760. Longest Even Odd Subarray With Threshold

2760. Longest Even Odd Subarray With Threshold

Question

You are given a 0-indexed integer array nums and an integer threshold.

Find the length of the longest subarray of numsstarting at index l and ending at index r (0 <= l <= r < nums.length) that satisfies the following conditions:

  • nums[l] % 2 == 0
  • For all indices i in the range [l, r - 1]nums[i] % 2 != nums[i + 1] % 2
  • For all indices i in the range [l, r]nums[i] <= threshold
Read more
1456. Maximum Number of Vowels in a Substring of Given Length

2321. Maximum Score Of Spliced Array

Question

You are given two 0-indexed integer arrays nums1 and nums2, both of length n.

You can choose two integers left and right where 0 <= left <= right < n and swap the subarray nums1[left...right] with the subarray nums2[left...right].

  • For example, if nums1 = [1,2,3,4,5] and nums2 = [11,12,13,14,15] and you choose left = 1 and right = 2, nums1 becomes [1,<strong><u>12,13</u></strong>,4,5] and nums2 becomes [11,<strong><u>2,3</u></strong>,14,15].

You may choose to apply the mentioned operation once or not do anything.

The score of the arrays is the maximum of sum(nums1) and sum(nums2), where sum(arr) is the sum of all the elements in the array arr.

Return the maximum possible score.

A subarray is a contiguous sequence of elements within an array. arr[left...right] denotes the subarray that contains the elements of nums between indices left and right (inclusive).

Solution

滑动窗口。一开始想用dp解决的。
后来发现可以优化到直接记录连续的最大差值即可。
以后有提到连续的一定要想到滑动窗口实现。

辅助方法getMinContigouousDiff()用来计算两个数组间连续的最小的差值和。
当sum为负数时,窗口向右侧扩展,将新的位置加入sum,并更新最小值min。
当扩展后sum大于0时,则放弃之前的所有元素,将sum归零。
最后返回最小值min。

计算两个数组的所有数字之和t1和t2。分别计算nums1[]和nums2[]的差min1和nums2[]和nums1[]的差min2。
在其中替换连续的部分则为t1-min1,t2-min2。取两者中较大的数字返回即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public int maximumsSplicedArray(int[] nums1, int[] nums2) {
int n = nums1.length;
int t1 = 0, t2 = 0;
for(int i = 0; i < n ;i++){ //计算两个数组未替换前的和
t1 += nums1[i];
t2 += nums2[i];
}
int min1 = getMinContiguousDiff(nums2, nums1);
int min2 = getMinContiguousDiff(nums1, nums2);
return Math.max(t1 - min1, t2 - min2); //替换连续的部分,两者中取最大值
}

private int getMinContiguousDiff(int[] nums1, int[] nums2){ //滑动窗口,求连续元素间的最小差值
int sum = 0, min = 0;
for(int i = 0; i < nums1.length; i++){
if(sum <= 0){ //当sum为负数时,窗口向右侧扩展,将新的位置加入sum
int diff = nums2[i] - nums1[i];
sum += diff;
if(diff < 0) min = Math.min(min, sum); //更新最小值
if(sum > 0) sum = 0; //当sum为正数时,抛弃前面的元素,重新开始计算sum
}
}
return min;
}
}

1423. Maximum Points You Can Obtain from Cards

Question

There are several cards arranged in a row, and each card has an associated number of points. The points are given in the integer array cardPoints.

In one step, you can take one card from the beginning or from the end of the row. You have to take exactly k cards.

Your score is the sum of the points of the cards you have taken.

Given the integer array cardPoints and the integer k, return the maximum score you can obtain.

Solution 1

这种取两端的问题可以转化为取中间连续k个元素之和最小。

计算所有数字的和sum。
然后用维护一个宽度为k的窗口内的加和temp,并记录其最小值min。

返回sum - min,结果即是取两侧时可以取的最大值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int maxScore(int[] cardPoints, int k) {
int n = cardPoints.length, sum = 0;
for(int point : cardPoints) sum += point; //求所有数值的和
if(k == n) return sum;
int left = 0, right = n-k, temp = 0, min = 0;

for(int i = 0; i < n-k; i++){
temp += cardPoints[i];
min = temp;
}
while(right < n){ //维护窗口内的temp,并更新min
temp += cardPoints[right] - cardPoints[left];;
min = Math.min(min, temp);
right++;
left++;
}
return sum - min;
}
}

Solution 2

分别计算从两侧开始的加和并记录在两个数组left[]和right[]上。

然后分别取两个指针,左侧指针l从0开始遍历到k,右侧指针对应的位置为l+n-k。计算并更新最大值max。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int maxScore(int[] cardPoints, int k) {
int n = cardPoints.length, max = 0;
int[] left = new int[n+1], right = new int[n+1];

for(int i = 0; i < n; i++){
left[i+1] = left[i] + cardPoints[i];
right[n-i-1] = right[n-i] + cardPoints[n-i-1];
}

for(int l = 0; l <= k; l++){
max = Math.max(max, left[l] + right[n-k+l]);
}

return max;
}
}
1695. Maximum Erasure Value

1695. Maximum Erasure Value

Question

You are given an array of positive integers nums and want to erase a subarray containing unique elements. The score you get by erasing the subarray is equal to the sum of its elements.

Return the maximum score you can get by erasing exactly one subarray.

An array b is called to be a subarray of a if it forms a contiguous subsequence of a, that is, if it is equal to a[l],a[l+1],...,a[r] for some (l,r).

Solution

滑动窗口+数组统计。

用sum记录窗口内的和,max记录和的最大值。
当新滑入的右侧指针不为0时,从左侧滑出元素并将滑出的元素改为0。
然后将右侧指针指针对应的数组统计改为1并更新当前的和max。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public int maximumUniqueSubarray(int[] nums) {
int[] bin = new int[10001];
int left = 0, max = 0, sum = 0;

for(int right = 0; right < nums.length; right++){
sum += nums[right];
while(bin[nums[right]] != 0){
bin[nums[left]] = 0;
sum -= nums[left];
left++;
}
bin[nums[right]] = 1;
max = Math.max(max, sum);
}
return max;
}
}

2302. Count Subarrays With Score Less Than K

Question

The score of an array is defined as the product of its sum and its length.

  • For example, the score of [1, 2, 3, 4, 5] is (1 + 2 + 3 + 4 + 5) * 5 = 75.

Given a positive integer array nums and an integer k, return the number of non-empty subarrays of nums whose score is strictly less than k.

A subarray is a contiguous sequence of elements within an array.

Solution

第一次在contest中做出hard题目,而且还是超过100%,庆祝一下!(不过因为前一道题做的太久没提交上去……

滑动窗口

数组内全部为正整数,当选择的子数组的尺寸增加时,其乘积是单调递增的。因此可以采用滑动窗口,在循环时维护窗口内数字的和sum和当前的乘积product。

每次将一个新的右侧元素滑入窗口,更新窗口内的sum值,并计算product值。当当前product大于k时,则将左侧的元素滑出窗口,并更新sum和product值。

调整完窗口尺寸后,由于新的right位置可以和前面的每一个子数组组成一个新的数组,因此将count加上当前的left到right的个数即可。

循环结束后返回count。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public long countSubarrays(int[] nums, long k) {
long count = 0, sum = 0;
int left = 0, right = 0;

while(right < nums.length){
sum += nums[right]; //右侧滑入窗口
long product = sum * (right - left + 1);

while(product >= k){ //当乘积大于k时,左侧滑出窗口
sum -= nums[left];
left++;
product = sum * (right - left + 1);
}
count += right - left + 1; //计算新的right位置可组成的新组合
right++;
}
return count;
}
}
1658. Minimum Operations to Reduce X to Zero

1658. Minimum Operations to Reduce X to Zero

Question

You are given an integer array nums and an integer x. In one operation, you can either remove the leftmost or the rightmost element from the array nums and subtract its value from x. Note that this modifies the array for future operations.

Return *the minimum number of operations to reduce x to exactly 0 if it is possible, otherwise, return *-1.

Solution

将原有问题的从两边减去一定的值,寻找加和为x的问题转换为寻找滑动窗口中的总和为total - x的新问题。

滑动窗口

初始化当前窗口内的sum,左侧指针left与右侧指针right。
每次将一个新的元素nums[right]加入窗口范围。

当当前的sum大于寻找的target,则将nums[left]滑出窗口,更新left与sum的值。

如果当前窗口内的sum等于寻找的target,则更新记录最小操作次数的min。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int minOperations(int[] nums, int x) {
int total = 0, min = Integer.MAX_VALUE;
for(int num : nums){
total += num;
}
int target = total - x; //将减去两侧元素并寻找和为x的结果的问题转换为用滑动窗口寻找total - x的结果。
int sum = 0, left = 0, right = 0;
while(right < nums.length){
sum += nums[right]; //调整右侧范围,加入新元素到窗口
right++;

while(left < right && sum > target){ //如果窗口内的和大于目标,则调整左侧范围
sum -= nums[left];
left++;
}
if(sum == target){ //如果窗口内的和等于目标,则更新最小操作次数
min = Math.min(min, nums.length - (right - left));
}
}
return min == Integer.MAX_VALUE ? -1 : min;
}
}

187. Repeated DNA Sequences

The DNA sequence is composed of a series of nucleotides abbreviated as ‘A’, ‘C’, ‘G’, and ‘T’.

  • For example, “ACGAATTCCG” is a DNA sequence.
    When studying DNA, it is useful to identify repeated sequences within the DNA.

Given a string s that represents a DNA sequence, return all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule. You may return the answer in any order.

哈希表 + 滑动窗口 + 位操作。
将四个字符映射到四个二进制字符上。这样字符串就可以用20bit表示。
这样就可以用一个整数来表示这四个字符。
然后采用哈希表记录出现次数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
static final int L = 10;
public List<String> findRepeatedDnaSequences(String s) {
List<String> ret = new ArrayList<String>();
if(s.length() < L) return ret;
HashMap<Integer, Integer> map = new HashMap<>();
int left = 0;
int right = 10;

int[] bin = new int[26];
bin['A'-'A'] = 0;
bin['T'-'A'] = 1;
bin['C'-'A'] = 2;
bin['G'-'A'] = 3;

int x = 0;
for(int i = 0; i < L-1; i++){
x = (x << 2) | bin[s.charAt(i)-'A'];
}

for(int i = 0; i <= s.length() - L; i++){
x = ((x << 2) | bin[s.charAt(i + L -1)-'A']) & ((1 << L * 2) - 1);
map.put(x, map.getOrDefault(x, 0)+1);
if(map.get(x) == 2) ret.add(s.substring(i, i+L));
}
return ret;
}
}

遍历将字字符串加入哈希表并记录出现次数,然后返回出现次数大于1的字符串。
注意在循环时就可以直接添加结果到列表,这样可以减少操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public List<String> findRepeatedDnaSequences(String s) {
List<String> ret = new ArrayList<String>();
HashMap<String, Integer> map = new HashMap<>();
int left = 0;
int right = 10;

while(right <= s.length()){
String sub = s.substring(left, right);
map.put(sub, map.getOrDefault(sub, 0)+1);
if(map.get(sub) == 2) ret.add(sub);
left++;
right++;
}
return ret;
}
}

713. Subarray Product Less Than K

Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k.

滑动窗口,维护一个窗口内的乘积。
当乘积小于目标值时,窗口右侧向右移动。每加入一个新数值,可以增加(j-i+1)个组合。
当乘积大于目标时,窗口左侧向右移动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int i = 0;
int j = 0;
int product = nums[0];
int count = 0;

while(i < nums.length && j < nums.length){
if(product < k){
count += (j - i) + 1;
j++;
if(j < nums.length ) product *= nums[j];
}
else{
product /= nums[i];
i++;
}
}
return count;
}
}

209. Minimum Size Subarray Sum

Given an array of positive integers nums and a positive integer target, return the minimal length of a contiguous subarray [numsl, numsl+1, …, numsr-1, numsr] of which the sum is greater than or equal to target. If there is no such subarray, return 0 instead.

滑动窗口,先取最左侧数字。记录窗口的最小值。
如果小于目标值,则右侧窗口向右移动,扩大窗口。更新窗口内的值。
如果大于等于目标值,则左侧窗口向右移动,缩小窗口。更新窗口内的值。
同时如果窗口大小小于最小值则更新窗口最小值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int min = Integer.MAX_VALUE;
int left = 0;
int right = 0;
int sum = nums[0];
while(right < nums.length){
if(sum >= target){
min = Math.min(min, right - left + 1);
sum -= nums[left];
left++;
}
else{
right++;
if(right < nums.length) sum += nums[right];
}
}
return min == Integer.MAX_VALUE ? 0 : min;
}
}

计算前缀和。
前缀和[j]与前缀和的[i]的差就是i+1到j的和。
因此需要找到sum[j] - sum[i] >= k。
暴力枚举的话需要O(n^2^)的时间复杂度。

由于前缀和是有序的,因此我们可以采用二分搜索。
寻找sum[j] - k >= sum[i]。
Arrays.binarySearch方法可以返回查找到的目录。
如果没有该值,方法会返回一个负数,其取反(x取反相当于[-(x+1)])的值就是应该插入的目录。
将ans设置为无限大,如果(index - i)小于最小值则更新到ans。

时间复杂度O(nlogn)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int[] sum = new int[nums.length+1];
for(int i = 1; i <= nums.length; i++){
sum[i] = sum[i-1] + nums[i-1];
}

int ans = Integer.MAX_VALUE;
for(int i = 0; i < sum.length; i++){
int search = sum[i] + target;
int index = Arrays.binarySearch(sum, search);
if(index < 0){
index = ~index;
}
if(index < sum.length){
ans = Math.min(ans, index - i);
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
}

438. Find All Anagrams in a String

Given two strings s and p, return an array of all the start indices of p’s anagrams in s. You may return the answer in any order.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

滑动窗口。采用两个数组分别计算两个字符串中字符出现的数量。

滑动窗口时,维护数组中字符串中字符出现的次数。
循环,判断两个字符串是否相等,如果相等,则将当前的左指针添加进答案。
移动窗口的左右指针,在数组中减少前一项出现的次数,增加后一项出现的次数。

通过维护一个表示两个字符串中字符差值的数组,可以将算法优化算法,而不必进行两个数组的比较。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> ans = new ArrayList();
if(p.length()>s.length()){
return ans;
}

int[] alphabet = new int[26];
int[] window = new int[26];

for(int i = 0; i < p.length(); i++){
alphabet[p.charAt(i) - 'a']++;
window[s.charAt(i) - 'a']++;
}

int i = 0;
int j = p.length();

while(j < s.length()){
if(Arrays.equals(alphabet, window)){
ans.add(i);
}
window[s.charAt(i) - 'a']--;
window[s.charAt(j) - 'a']++;
i++;
j++;
}

if(Arrays.equals(alphabet, window)){
ans.add(i);
}

return ans;

}
}
3. Longest Substring Without Repeating Characters

3. Longest Substring Without Repeating Characters

Question

Given a string s, find the length of the longest substring without repeating characters.

Solution 1

滑动窗口加数组统计。
用一个数组bin[]记录各个字符的访问情况。(数组尺寸由字符串的总数决定)

当指针j第一次遍历到一个字符时,如果该字符对应的位置为0,则将其设置为1。
否则对前面的i指针进行遍历,并将i经过的位置重新设置为0。如果i的字符与当前j字符相等,则将bin[index]设置为0,计算当前的长度j-i并更新最大值best。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int lengthOfLongestSubstring(String s) {
int best = 0, i = 0, j = 0;
char[] word = s.toCharArray();
int[] bin = new int[128];
while(j < s.length()){
int index = word[j] - ' ';
if(bin[index] == 0){
bin[index] = 1;
j++;
}
else{
while(i < j){
int temp = word[i] - ' ';
if(temp == index && bin[index] == 1){
bin[index] = 0;
best = Math.max(best, j - i);
i++;
break;
}
bin[temp] = 0;
i++;
}
}
best = Math.max(best, j - i);
}
return best;
}
}

Solution 2

滑动窗口,哈希表记录访问过的字符的元素。
如果重复,则放弃前一个重复的字符,更新左指针。
注意:只有在新指针大于现有指针时才更新!

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int lengthOfLongestSubstring(String s) {
int best = 0;
int i = 0;
int j = 0;

HashMap<Character, Integer> map = new HashMap<Character, Integer>();

while ( j < s.length() ){
char curChar = s.charAt(j);
if ( !map.containsKey(curChar) ){
map.put( curChar, j );
}
else{
if ( map.get(curChar) + 1 > i){
i = map.get(curChar) + 1;
}

map.put( curChar, j );
}

if ((j - i + 1) > best){
best = (j - i + 1);
}
j++;
}
return best;
}
}