LeetCode XOR Operation in an Array

1486. XOR Operation in an Array

Given an integer n and an integer start.

Define an array nums where nums[i] = start + 2*i (0-indexed) and n == nums.length.

Return the bitwise XOR of all elements of nums.

Example 1:

Input: n = 5, start = 0
Output: 8
Explanation: Array nums is equal to [0, 2, 4, 6, 8] where (0 ^ 2 ^ 4 ^ 6 ^ 8) = 8.
Where "^" corresponds to bitwise XOR operator.

Example 2:

Input: n = 4, start = 3
Output: 8
Explanation: Array nums is equal to [3, 5, 7, 9] where (3 ^ 5 ^ 7 ^ 9) = 8.

Example 3:

Input: n = 1, start = 7
Output: 7

Example 4:

Input: n = 10, start = 5
Output: 2

Constraints:

  • 1 <= n <= 1000
  • 0 <= start <= 1000
  • n == nums.length

给定start和n,将所有的start+2i进行异或。简单题,直接照做:

class Solution {
public:
	int xorOperation(int n, int start) {
		int ans = 0;
		for (int i = 0; i < n; ++i) {
			ans ^= (start + 2 * i);
		}
		return ans;
	}
};

本代码提交AC,用时0MS。

LeetCode Minimum Number of Days to Make m Bouquets

5455. Minimum Number of Days to Make m Bouquets

Given an integer array bloomDay, an integer m and an integer k.

We need to make m bouquets. To make a bouquet, you need to use k adjacent flowers from the garden.

The garden consists of n flowers, the ith flower will bloom in the bloomDay[i] and then can be used in exactly one bouquet.

Return the minimum number of days you need to wait to be able to make m bouquets from the garden. If it is impossible to make m bouquets return -1.

Example 1:

Input: bloomDay = [1,10,3,10,2], m = 3, k = 1
Output: 3
Explanation: Let's see what happened in the first three days. x means flower bloomed and _ means flower didn't bloom in the garden.
We need 3 bouquets each should contain 1 flower.
After day 1: [x, _, _, _, _]   // we can only make one bouquet.
After day 2: [x, _, _, _, x]   // we can only make two bouquets.
After day 3: [x, _, x, _, x]   // we can make 3 bouquets. The answer is 3.

Example 2:

Input: bloomDay = [1,10,3,10,2], m = 3, k = 2
Output: -1
Explanation: We need 3 bouquets each has 2 flowers, that means we need 6 flowers. We only have 5 flowers so it is impossible to get the needed bouquets and we return -1.

Example 3:

Input: bloomDay = [7,7,7,7,12,7,7], m = 2, k = 3
Output: 12
Explanation: We need 2 bouquets each should have 3 flowers.
Here's the garden after the 7 and 12 days:
After day 7: [x, x, x, x, _, x, x]
We can make one bouquet of the first three flowers that bloomed. We cannot make another bouquet from the last three flowers that bloomed because they are not adjacent.
After day 12: [x, x, x, x, x, x, x]
It is obvious that we can make two bouquets in different ways.

Example 4:

Input: bloomDay = [1000000000,1000000000], m = 1, k = 1
Output: 1000000000
Explanation: You need to wait 1000000000 days to have a flower ready for a bouquet.

Example 5:

Input: bloomDay = [1,10,2,9,3,8,4,7,5,6], m = 4, k = 2
Output: 9

Constraints:

  • bloomDay.length == n
  • 1 <= n <= 10^5
  • 1 <= bloomDay[i] <= 10^9
  • 1 <= m <= 10^6
  • 1 <= k <= n

花园里有n朵花,告诉你每朵花在第几天开花。现在要扎m束花,每束花必须由相邻的k朵花组成,问要完成这个任务,最少需要等多少天。

首先看看需要的总花数m*k是否>n,如果是的话,即使所有花都开了也无法完成任务。

对于能够完成的任务,要求最少等待天数。暴力方法是,枚举bloomDay数组中所有unique的天数,对于每一天,都看看能否完成任务,方法是把数组中相邻的开的花每k多组成一束,看看能否组成m束。

但是暴力方法超时,一个改进的方法是二分。因为天数是递增的,而且每增加一天,开花的总数也是递增的,相当于能组成的花束也是递增的(严格来说是非递减的),这就为二分搜索创造了条件。

维护两个数组,days存储了unique的天数,递增排序(map自动递增有序了);num_bloomed表示在当前天数下,开花的总数,随着days的增大, num_bloomed 存储的开花总数也是递增的。在构造 num_bloomed 的过程中,可以求到最小的可能的开始天数left,然后在left和right中二分搜索合法的最小天数。

完整代码如下:

class Solution {
	bool IsValid(vector<int>& bloomDay, int day, int m, int k) {
		int bouquets = 0, n = bloomDay.size();
		int i = 0, good = 0;
		while (i < n) {
			while (i<n&&bloomDay[i]>day)++i;
			if (i >= n)break;

			int j = i;
			while (j < n&&bloomDay[j] <= day)++j;
			// [i,j) 都开花了

			good += (j - i) / k;

			i = j;
		}
		return good >= m;
	}
public:
	int minDays(vector<int>& bloomDay, int m, int k) {
		int n = bloomDay.size();
		if (m*k > n)return -1;

		map<int, int> hash;
		for (int i = 0; i < n; ++i)++hash[bloomDay[i]];
		if (m*k == n)return (--hash.end())->first; // 最后一天

		int unique_day = hash.size();
		vector<int> days(unique_day, 0);
		vector<int> num_bloomed(unique_day, 0);

		int d = 0, nb = 0;
		int left = 0, right = unique_day - 1, found_left = false;
		for (map<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
			nb += it->second;
			if (nb >= m * k && !found_left) {
				left = d;
				found_left = true;
			}

			days[d] = it->first;
			num_bloomed[d] = nb;
			
			++d;
		}

		while (left <= right) {
			int mid = left + (right - left) / 2;
			bool valid = IsValid(bloomDay, days[mid], m, k);
			if (valid)right = mid - 1;
			else left = mid + 1;
		}

		return days[right + 1];
	}
};

本代码提交AC,用时1316MS。

Leetcode Least Number of Unique Integers after K Removals

5454. Least Number of Unique Integers after K Removals

Given an array of integers arr and an integer k. Find the least number of unique integers after removing exactly k elements.

Example 1:

Input: arr = [5,5,4], k = 1
Output: 1
Explanation: Remove the single 4, only 5 is left.

Example 2:

Input: arr = [4,3,1,1,3,3,2], k = 3
Output: 2
Explanation: Remove 4, 2 and either one of the two 1s or three 3s. 1 and 3 will be left.

Constraints:

  • 1 <= arr.length <= 10^5
  • 1 <= arr[i] <= 10^9
  • 0 <= k <= arr.length

给定一个数组,问从中删掉K个数后,剩余数中unique数的个数最少是多少。

要让剩余的unique数最少,则删掉的数必须是频率最低的数。举个例子,原数组是[1,2,3,3,3],如果删掉两个数,肯定是删掉1,2,这样剩余是[3,3,3],unique数只有3这一个数。如果删掉两个3,则剩余[1,2,3],unique数有3个。

所以,对所有数按其频率从小到大排序,然后删掉频率最低的前几个数即可。完整代码如下:

class Solution {
public:
	int findLeastNumOfUniqueInts(vector<int>& arr, int k) {
		int n = arr.size();
		vector<pair<int, int>> num_counts;
		unordered_map<int, int> hash;
		for (int i = 0; i < n; ++i)++hash[arr[i]];
		for (unordered_map<int, int>::iterator it = hash.begin(); it != hash.end(); ++it) {
			num_counts.push_back(make_pair(it->second, it->first));
		}
		sort(num_counts.begin(), num_counts.end());
		int m = num_counts.size(), sum = 0;

		if (k == 0)return m;
		else if (k == n)return 0;

		for (int i = 0; i < m; ++i) {
			sum += num_counts[i].first;
			if (sum == k)return m - i - 1;
			else if (sum > k)return m - i;
		}

		return 0;
	}
};

本代码提交AC,用时792MS。

Leetcode Running Sum of 1d Array

5453. Running Sum of 1d Array

Given an array nums. We define a running sum of an array as runningSum[i] = sum(nums[0]…nums[i]).

Return the running sum of nums.

Example 1:

Input: nums = [1,2,3,4]
Output: [1,3,6,10]
Explanation: Running sum is obtained as follows: [1, 1+2, 1+2+3, 1+2+3+4].

Example 2:

Input: nums = [1,1,1,1,1]
Output: [1,2,3,4,5]
Explanation: Running sum is obtained as follows: [1, 1+1, 1+1+1, 1+1+1+1, 1+1+1+1+1].

Example 3:

Input: nums = [3,1,2,10,1]
Output: [3,4,6,16,17]

Constraints:

  • 1 <= nums.length <= 1000
  • -10^6 <= nums[i] <= 10^6

给定一个数组,求出数组的前缀和。简单题,代码如下:

class Solution {
public:
	vector<int> runningSum(vector<int>& nums) {
		int n = nums.size();
		vector<int> ans(n, 0);
		ans[0] = nums[0];
		for (int i = 1; i < n; ++i)ans[i] = ans[i - 1] + nums[i];
		return ans;
	}
};

本代码提交AC,用时12MS。

LeetCode Max Dot Product of Two Subsequences

1458. Max Dot Product of Two Subsequences

Given two arrays nums1 and nums2.

Return the maximum dot product between non-empty subsequences of nums1 and nums2 with the same length.

A subsequence of a array is a new array which is formed from the original array by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, [2,3,5] is a subsequence of [1,2,3,4,5] while [1,5,3] is not).

Example 1:

Input: nums1 = [2,1,-2,5], nums2 = [3,0,-6]
Output: 18
Explanation: Take subsequence [2,-2] from nums1 and subsequence [3,-6] from nums2.
Their dot product is (2*3 + (-2)*(-6)) = 18.

Example 2:

Input: nums1 = [3,-2], nums2 = [2,-6,7]
Output: 21
Explanation: Take subsequence [3] from nums1 and subsequence [7] from nums2.
Their dot product is (3*7) = 21.

Example 3:

Input: nums1 = [-1,-1], nums2 = [1,1]
Output: -1
Explanation: Take subsequence [-1] from nums1 and subsequence [1] from nums2.
Their dot product is -1.

Constraints:

  • 1 <= nums1.length, nums2.length <= 500
  • -1000 <= nums1[i], nums2[i] <= 1000

给定两个数组,问从这两个数组中选出两个长度相同的子序列,使得子序列的向量点积最大,求这个最大值。

难题,参考讨论区解法: https://leetcode.com/problems/max-dot-product-of-two-subsequences/discuss/648261/C++Python-Simple-DP

假设dp[i][j]表示nums1[0:i]和nums2[0:j]的最大点积。则在求解dp[i][j]时,有以下四种情况:

  • 不选nums1第i个数,dp[i][j]=dp[i-1][j]
  • 不选nums2第j个数,dp[i][j]=dp[i][j-1]
  • 既不选nums1第i个数,也不选nums2第j个数,dp[i][j]=dp[i-1][j-1]
  • 既选nums1第i个数,也选nums2第j个数,dp[i][j]=dp[i-1][j-1]+nums1[i]*nums2[j]

在最后一个情况中,如果dp[i-1][j-1]<0的话,就把前面的点积都扔掉,相当于max(dp[i-1][j-1],0),完整代码如下:

class Solution {
public:
	int maxDotProduct(vector<int>& nums1, vector<int>& nums2) {
		int m = nums1.size(), n = nums2.size();
		vector<vector<int>> dp(m + 1, vector<int>(n + 1, INT_MIN));
		for (int i = 1; i <= m; ++i) {
			for (int j = 1; j <= n; ++j) {
				dp[i][j] = max(dp[i][j], dp[i - 1][j]);
				dp[i][j] = max(dp[i][j], dp[i][j - 1]);
				dp[i][j] = max(dp[i][j], dp[i - 1][j - 1]);
				dp[i][j] = max(dp[i][j], max(dp[i - 1][j - 1], 0) + nums1[i - 1] * nums2[j - 1]);
			}
		}
		return dp[m][n];
	}
};

本代码提交AC,用时96MS。

LeetCode Pseudo-Palindromic Paths in a Binary Tree

5418. Pseudo-Palindromic Paths in a Binary Tree

Given a binary tree where node values are digits from 1 to 9. A path in the binary tree is said to be pseudo-palindromic if at least one permutation of the node values in the path is a palindrome.

Return the number of pseudo-palindromic paths going from the root node to leaf nodes.

Example 1:

Input: root = [2,3,1,3,1,null,1]
Output: 2 
Explanation: The figure above represents the given binary tree. There are three paths going from the root node to leaf nodes: the red path [2,3,3], the green path [2,1,1], and the path [2,3,1]. Among these paths only red path and green path are pseudo-palindromic paths since the red path [2,3,3] can be rearranged in [3,2,3] (palindrome) and the green path [2,1,1] can be rearranged in [1,2,1] (palindrome).

Example 2:

Input: root = [2,1,1,1,3,null,null,null,null,null,1]
Output: 1 
Explanation: The figure above represents the given binary tree. There are three paths going from the root node to leaf nodes: the green path [2,1,1], the path [2,1,3,1], and the path [2,1]. Among these paths only the green path is pseudo-palindromic since [2,1,1] can be rearranged in [1,2,1] (palindrome).

Example 3:

Input: root = [9]
Output: 1

Constraints:

  • The given binary tree will have between 1 and 10^5 nodes.
  • Node values are digits from 1 to 9.

给定一棵二叉树,问树中从根到叶子的路径构成的一个数组,能调整成回文数组串的个数。

简单题,直接DFS,记录每条路径中包含1~9的个数,如果该数组中包含的奇数的个数不超过1个,则能调整成回文串,否则不能。

回文串有两种形式,一种是123321,即串中每个数字都对称出现偶数次,另一种是1234321,中间的4只出现1次,其他数字都出现偶数次。

完整代码如下:

class Solution {
private:
	void DFS(TreeNode* root, vector<int> &hash, int &ans) {
		if (root == NULL)return;
		++hash[root->val];

		if (root->left == NULL && root->right == NULL) {
			int odd = 0;
			for (int i = 1; i <= 9; ++i) {
				if (hash[i] % 2 == 1) {
					++odd;
					if (odd > 1) {
						break;
					}
				}
			}
			if (odd <= 1) {
				++ans;
			}
			--hash[root->val];
			return;
		}

		DFS(root->left, hash, ans);
		DFS(root->right, hash, ans);
		--hash[root->val];
	}
public:
	int pseudoPalindromicPaths(TreeNode* root) {
		vector<int> hash(10, 0);
		int ans = 0;
		DFS(root, hash, ans);
		return ans;
	}
};

本代码提交AC。

LeetCode Maximum Number of Vowels in a Substring of Given Length

5417. Maximum Number of Vowels in a Substring of Given Length

Given a string s and an integer k.

Return the maximum number of vowel letters in any substring of s with length k.

Vowel letters in English are (a, e, i, o, u).

Example 1:

Input: s = "abciiidef", k = 3
Output: 3
Explanation: The substring "iii" contains 3 vowel letters.

Example 2:

Input: s = "aeiou", k = 2
Output: 2
Explanation: Any substring of length 2 contains 2 vowels.

Example 3:

Input: s = "leetcode", k = 3
Output: 2
Explanation: "lee", "eet" and "ode" contain 2 vowels.

Example 4:

Input: s = "rhythms", k = 4
Output: 0
Explanation: We can see that s doesn't have any vowel letters.

Example 5:

Input: s = "tryhard", k = 4
Output: 1

Constraints:

  • 1 <= s.length <= 10^5
  • s consists of lowercase English letters.
  • 1 <= k <= s.length

给定字符串s和长度k,问s中长度为k的子串中,包含最多元音字母的个数。

简单题,直接滑动窗口+双指针计数即可:

class Solution {
public:
	bool IsVol(const char &c) {
		return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
	}
	int maxVowels(string s, int k) {
		int ans = 0;
		int i = 0, j = k - 1, n = s.size();
		for (int k = i; k <= j; ++k) {
			if (IsVol(s[k]))++ans;
		}
		int cur = ans;
		while (j < n - 1) {
			++j;
			if (IsVol(s[j]))++cur;
			if (IsVol(s[i]))--cur;
			++i;
			ans = max(ans, cur);
		}
		return ans;
	}
};

本代码提交AC。

LeetCode Check If a Word Occurs As a Prefix of Any Word in a Sentence

5416. Check If a Word Occurs As a Prefix of Any Word in a Sentence

Given a sentence that consists of some words separated by a single space, and a searchWord.

You have to check if searchWord is a prefix of any word in sentence.

Return the index of the word in sentence where searchWord is a prefix of this word (1-indexed).

If searchWord is a prefix of more than one word, return the index of the first word (minimum index). If there is no such word return -1.

prefix of a string S is any leading contiguous substring of S.

Example 1:

Input: sentence = "i love eating burger", searchWord = "burg"
Output: 4
Explanation: "burg" is prefix of "burger" which is the 4th word in the sentence.

Example 2:

Input: sentence = "this problem is an easy problem", searchWord = "pro"
Output: 2
Explanation: "pro" is prefix of "problem" which is the 2nd and the 6th word in the sentence, but we return 2 as it's the minimal index.

Example 3:

Input: sentence = "i am tired", searchWord = "you"
Output: -1
Explanation: "you" is not a prefix of any word in the sentence.

Example 4:

Input: sentence = "i use triple pillow", searchWord = "pill"
Output: 4

Example 5:

Input: sentence = "hello from the other side", searchWord = "they"
Output: -1

Constraints:

  • 1 <= sentence.length <= 100
  • 1 <= searchWord.length <= 10
  • sentence consists of lowercase English letters and spaces.
  • searchWord consists of lowercase English letters.

给定一个句子和一个待搜索词,问该搜索词是否可能是这个句子中某个词的前缀。

直接暴力搜索即可,因为指定必须是前缀,所以其实直接截取相同长度的前缀判断相等即可。

class Solution {
public:
	int isPrefixOfWord(string sentence, string searchWord) {
		int idx = 1, i = 0, j = 0, n = sentence.size();
		while (i < n) {
			j = i + 1;
			while (j < n&&sentence[j] != ' ')++j;
			string cur = sentence.substr(i, j - i);
			size_t pos = cur.find(searchWord);
			if (pos != string::npos&&pos == 0) {
				return idx;
			}
			++idx;
			i = j + 1;
		}
		return -1;
	}
};

本代码提交AC。

LeetCode Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit

1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit

Given an array of integers nums and an integer limit, return the size of the longest continuous subarray such that the absolute difference between any two elements is less than or equal to limit.

In case there is no subarray satisfying the given condition return 0.

Example 1:

Input: nums = [8,2,4,7], limit = 4
Output: 2 
Explanation: All subarrays are: 
[8] with maximum absolute diff |8-8| = 0 <= 4.
[8,2] with maximum absolute diff |8-2| = 6 > 4. 
[8,2,4] with maximum absolute diff |8-2| = 6 > 4.
[8,2,4,7] with maximum absolute diff |8-2| = 6 > 4.
[2] with maximum absolute diff |2-2| = 0 <= 4.
[2,4] with maximum absolute diff |2-4| = 2 <= 4.
[2,4,7] with maximum absolute diff |2-7| = 5 > 4.
[4] with maximum absolute diff |4-4| = 0 <= 4.
[4,7] with maximum absolute diff |4-7| = 3 <= 4.
[7] with maximum absolute diff |7-7| = 0 <= 4. 
Therefore, the size of the longest subarray is 2.

Example 2:

Input: nums = [10,1,2,4,7,2], limit = 5
Output: 4 
Explanation: The subarray [2,4,7,2] is the longest since the maximum absolute diff is |2-7| = 5 <= 5.

Example 3:

Input: nums = [4,2,2,2,4,4,2,2], limit = 0
Output: 3

Constraints:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^9
  • 0 <= limit <= 10^9

给定一个数组,问数组中一个连续的子数组内的最大值和最小值的差不超过limit的最长连续子数组的长度。

常规解法。使用DP求出任何一个子区间dp[i,j]的最大值和最小值,dp[i,j+1]的最大值和最小值只需要用nums[j]和dp[i,j]的最大值和最小值比较即可。完整代码如下:

class Solution {
public:
	int longestSubarray(vector<int>& nums, int limit) {
		int n = nums.size();
		int ans = 1; // 最小值是1

		for (int i = 0; i < n; ++i) {
			vector<int> dp_max(n, 0), dp_min(n, INT_MAX);
			dp_max[i] = dp_min[i] = nums[i];
			for (int len = 2; len <= n; ++len) {
				int j = i + len - 1;
				if (j >= n)break;
				dp_max[j] = max(dp_max[j - 1], nums[j]);
				dp_min[j] = min(dp_min[j - 1], nums[j]);
				int diff = dp_max[j] - dp_min[j];
				if (diff <= limit) {
					ans = max(ans, j - i + 1);
				}
				else {
					break;
				}
			}
		}
		return ans;
	}
};

很不幸,该代码的时间复杂度达到了O(n^2),在最后两个测试样例上TLE了。

比O(n^2)低的复杂度有O(nlgn)和O(n)。O(nlgn)排序好像无法求解,那么只有O(n)的方法了。此题本质上是一个滑动窗口的问题,可以用两个指针i和j,标记区间的起止位置。那么问题的关键就是如何快速求解到区间[i,j]的最大值和最小值。

我之前的方法是用DP提前算出来,但是DP的过程费时。使用优先队列priority_queue(最大堆、最小堆)可以得到当前区域的最大值和最小值,但是一旦差的绝对值超过limit,需要从优先队列中删除i所指元素时,priority_queue没有提供erase或者remove之类的接口,所以使用priority_queue无法达成目标。

提示使用multiset,我之前知道multiset内部使用红黑树结构,内部存储是有序的,而且提供了erase接口以供删除元素。但是我一直不确定如何获得multiset内部的最大值和最小值元素,虽然它内部是有序的,但我不确定程序员能否得到其有序列表。

后来看了multiset的API,里面明确说明了multiset默认是从小到大排序,而且其begin()指向最小值rbegin()指向最后一个元素,即最大值。所以multiset是一个功能很强大的容器,它不但可以实现堆的功能,还能查找、删除某个元素,比 priority_queue 更强大。

使用multiset的完整代码如下:

class Solution {
public:
    int longestSubarray(vector<int>& A, int limit) {
        int res = 0, n = A.size(), i = 0, j;
        multiset<int> m;
        for (j = 0; j < n; ++j) {
            m.insert(A[j]);
            if (*m.rbegin() - *m.begin() > limit)
                m.erase(m.find(A[i++]));
            else
                res=max(res,j-i+1);
        }
        return res;
    }
};

本代码提交AC,用时368MS。

另外,我们还可以指定multiset内部的排序规则,让其表现为最大堆或最小堆。

LeetCode Check If All 1’s Are at Least Length K Places Away

1437. Check If All 1’s Are at Least Length K Places Away

Given an array nums of 0s and 1s and an integer k, return True if all 1’s are at least k places away from each other, otherwise return False.

Example 1:

Input: nums = [1,0,0,0,1,0,0,1], k = 2
Output: true
Explanation: Each of the 1s are at least 2 places away from each other.

Example 2:

Input: nums = [1,0,0,1,0,1], k = 2
Output: false
Explanation: The second 1 and third 1 are only one apart from each other.

Example 3:

Input: nums = [1,1,1,1,1], k = 0
Output: true

Example 4:

Input: nums = [0,1,0,1], k = 1
Output: true

Constraints:

  • 1 <= nums.length <= 10^5
  • 0 <= k <= nums.length
  • nums[i] is 0 or 1

给定一个0/1数组,问数组中所有1的间隔是否超过某个阈值。

简单题,一遍扫描数组,使用双指针记录相邻两个1,并实时计算它们的距离,完整代码如下:

class Solution {
public:
	bool kLengthApart(vector<int>& nums, int k) {
		int n = nums.size();
		int i = 0, j = 0;
		while (i < n) {
			while (i < n&&nums[i] == 0)++i;
			if (i >= n - 1)break;
			j = i + 1;
			while (j < n&&nums[j] == 0)++j;
			if (j - i - 1 < k)return false;
			i = j;
		}
		return true;
	}
};

本代码提交AC,用时140MS。