Category Archives: LeetCode

LeetCode Largest Divisible Subset

LeetCode Largest Divisible Subset Given a set of distinct positive integers, find the largest subset such that every pair (Si, Sj) of elements in this subset satisfies: Si % Sj = 0 or Sj % Si = 0. If there are multiple solutions, return any subset is fine. Example 1:

nums: [1,2,3]
Result: [1,2] (of course, [1,3] will also be ok)
Example 2:
nums: [1,2,4,8]
Result: [1,2,4,8]

给定一个数组,求满足这样一个条件的最大子集:该子集中的任意两个元素a和b满足a%b==0或者b%a==0。通过样例能够很好的理解这个题意。 这题的解法和最长上升子序列的求法很类似,用DP求解。首先对数组从小到大排序,然后对于第i个数nums[i],定义dp[i]表示包含nums[i]、满足divisible subset条件、且nums[i]在该子集中是最大元素的子集的最大元素个数。其实就是把dp[i]直观的理解为以nums[i]结尾的满足divisible subset条件的最大子集合的大小。 求解dp[i]的递归方法是,遍历比nums[i]小的所有数nums[j],如果nums[i]%nums[j],则说明nums[i]可以扔到nums[j]所在的子集合中,所以dp[i]=dp[j]+1。遍历所有的j,找一个最大的dp[i]。 因为本题要输出具体的解,所以还定义一个数组parent[i]表示dp[i]的最大值是从哪个j来的。最后输出最优解的时候,j不断的递归往前找即可。 完整代码如下: [cpp] class Solution { public: vector<int> largestDivisibleSubset(vector<int>& nums) { if (nums.empty())return{}; sort(nums.begin(), nums.end()); int n = nums.size(), maxLen = 0, maxIdx = 0; vector<int> dp(n, 1), parent(n, -1); for (int i = 0; i < n; ++i) { for (int j = i – 1; j >= 0; –j) { if (nums[i] % nums[j] == 0 && dp[i] < dp[j] + 1) { dp[i] = dp[j] + 1; parent[i] = j; } } if (dp[i] > maxLen) { maxLen = dp[i]; maxIdx = i; } } vector<int> ans; while (maxIdx != -1) { ans.push_back(nums[maxIdx]); maxIdx = parent[maxIdx]; } return ans; } }; [/cpp] 本代码提交AC,用时39MS。 参考:https://www.hrwhisper.me/leetcode-largest-divisible-subset/]]>

LeetCode Super Pow

LeetCode Super Pow Your task is to calculate ab mod 1337 where a is a positive integer and b is an extremely large positive integer given in the form of an array. Example1:

a = 2
b = [3]
Result: 8
Example2:
a = 2
b = [1,0]
Result: 1024

快速幂的进阶版,超级幂。要求$$a^B=a^{[b_0,b_1,b_2,…b_{n-1}]}$$,其中的B用一个数组来表示,数组中的元素都是个位数,分别从高位到低位。比如第二个样例表示的是$$2^10$$。 我一开始把$$a^B=a^{[b_0,b_1,b_2,…b_{n-1}]}$$展开成$$a^B=a^{b_0*10^{n-1}+b_1*10^{n-2}+…}$$,对于每一项都用快速幂算出$$10^{n-i-1}$$,然后用快速幂算出$$a^{b_i*10^{n-i-1}}$$,最后累乘起来。但是很不幸WA或者TLE。 后来看了网上的题解,发现真是巧妙,这不就类似于多项式计算时层层嵌套的方法吗,一时想不起来那个叫什么名字了。比如我们要算$$a^{[b_0,b_1,b_2]}$$,本来是$$a^{b_0*10^2+b_1*10+b_2}$$。但是我们计算是可以这样算,先算$$C_0=a^{b_0}$$;递归到$$b_1$$时,在算到$$b_0$$的基础上,有$$C_1=(a^{b_0})^{10}*a^{b_1}=C_0^{10}*a^{b_1}$$;递归到$$b_2$$时,有$$C_2=((a^{b_0})^{10}*a^{b_1})^{10}*a^{b_2}=C_1^{10}*a^{b_2}$$。把$$C_2$$展开其实就是$$a^{b_0*10^2+b_1*10+b_2}$$。 完整代码如下: [cpp] typedef unsigned long long ull; class Solution { private: const static int MOD = 1337; ull fastPow(ull x, ull y) { ull ans = 1; while (y) { if (y & 1)ans = (ans*x) % MOD; y >>= 1; x = (x*x) % MOD; } return ans; } public: int superPow(int a, vector<int>& b) { ull ans = 1; for (int i = 0; i < b.size(); ++i) { ans = fastPow(ans, 10)*fastPow(a, b[i]) % MOD; } return ans; } }; [/cpp] 本代码提交AC,用时9MS。]]>

LeetCode Bulls and Cows

299. Bulls and Cows299. Bulls and Cows

You are playing the following Bulls and Cows game with your friend: You write down a number and ask your friend to guess what the number is. Each time your friend makes a guess, you provide a hint that indicates how many digits in said guess match your secret number exactly in both digit and position (called “bulls”) and how many digits match the secret number but locate in the wrong position (called “cows”). Your friend will use successive guesses and hints to eventually derive the secret number.

Write a function to return a hint according to the secret number and friend’s guess, use A to indicate the bulls and B to indicate the cows. 

Please note that both secret number and friend’s guess may contain duplicate digits.

Example 1:

Input: secret = "1807", guess = "7810"

Output: "1A3B"

Explanation: 1 bull and 3 cows. The bull is 8, the cows are 0, 1 and 7.

Example 2:

Input: secret = "1123", guess = "0111"

Output: "1A1B"

Explanation: The 1st 1 in friend's guess is a bull, the 2nd or 3rd 1 is a cow.

Note: You may assume that the secret number and your friend’s guess only contain digits, and their lengths are always equal.299. Bulls and Cows


类似猜数字问题。假设秘密数字是”1807″,对方猜了一个数字是”7810″,问猜对了多少位,又有多少位是没猜对,但是秘密数字中出现了。比如这里猜对了第2位,都是’8’;’7’、’1’和’0’虽然没猜对位置,但是秘密数字中也有’1’、’0’和’7’,所以算完全猜对了1个数位,猜对了3个数,但位置不对,输出”1A3B”。 简单题,先对秘密数字建立数字和频率的hash表,然后遍历猜测数字,如果对应位相等,则完全猜对一个;如果对应位不相等,但是在秘密数字的hash表中,且频率大于0,则猜对第二类。 完整代码如下:

class Solution {
public:
    string getHint(string secret, string guess)
    {
        unordered_map<char, int> uci;
        for (const auto& c : secret)
            ++uci[c];
        int a = 0, b = 0;
        for (int i = 0; i < guess.size(); ++i) {
            if (guess[i] == secret[i]) {
                ++a;
                –uci[secret[i]];
            }
        }
        for (int i = 0; i < guess.size(); ++i) {
            if (guess[i] != secret[i] && uci[guess[i]] > 0) {
                ++b;
                –uci[guess[i]];
            }
        }
        return to_string(a) + "A" + to_string(b) + "B";
    }
};

本代码提交AC,用时6MS。需要注意的是,两类数字的判断需要分开,只有先判断了对应位数字完全一样的数位,才判断第二类位置不对但数对的情况。比如

Secret number:  "1122"
Friend's guess: "1222"

如果混在一起判断的话,对于guess中的第一个2,Secret是1,但Secret中有2,所以这个2被分类为位置错,但数字对的情况,从而Secret中的2的频率减少为1,导致最后一个2,虽然数字和位置完全一样,但是Secret中2的频率已经是0了,无法判断为A类。所以需要完全判断完A类之后,才判断B类。

二刷。因为只有0-9这10个数字,可以用数组代替hash:

class Solution {
public:
	string getHint(string secret, string guess) {
		int n = secret.size();
		int a = 0, b = 0;
		vector<int> s(10, 0), g(10, 0);
		for (int i = 0; i < n; ++i) {
			if (secret[i] == guess[i])++a;
			else {
				++s[secret[i] - '0'];
				++g[guess[i] - '0'];
			}
		}
		for (int i = 0; i < 10; ++i) {
			b += min(s[i], g[i]);
		}
		return to_string(a) + "A" + to_string(b) + "B";
	}
};

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

LeetCode Matchsticks to Square

LeetCode Matchsticks to Square Remember the story of Little Match Girl? By now, you know exactly what matchsticks the little match girl has, please find out a way you can make one square by using up all those matchsticks. You should not break any stick, but you can link them up, and each matchstick must be used exactly one time. Your input will be several matchsticks the girl has, represented with their stick length. Your output will either be true or false, to represent whether you could make one square using all the matchsticks the little match girl has. Example 1:

Input: [1,1,2,2,2]
Output: true
Explanation: You can form a square with length 2, one side of the square came two sticks with length 1.
Example 2:
Input: [3,3,3,3,4]
Output: false
Explanation: You cannot find a way to form a square with all the matchsticks.
Note:
  1. The length sum of the given matchsticks is in the range of 0 to 10^9.
  2. The length of the given matchstick array will not exceed 15.

卖火柴的小女孩有一堆长度不等的火柴棒,问用这些火柴棒能否围成一个正方形。 因为最多有15根火柴棒,所以可以DFS求解。 首先判断所有火柴棒的长度之和是否为4的倍数,如果是,则和除以4等于edge,也就是我们的目标是把所有火柴棒分成和等于edge的四等份。DFS的思路就是尝试把每根火柴棒放到第一、二、三、四份中,不断的递归求解。但是需要一些剪枝策略,否则会TLE。 先上代码: [cpp] class Solution { private: bool dfs(const vector<int>& nums, int idx, const int &edge, vector<int>& sums) { if (idx == nums.size() && sums[0] == sums[1] && sums[1] == sums[2] && sums[2] == sums[3])return true; if (idx == nums.size())return false; for (int i = 0; i < 4; ++i) { if (sums[i] + nums[idx] <= edge) { // (2) int j = i; while (–j >= 0) { if (sums[i] == sums[j])break; // (3) } if (j != -1)continue; sums[i] += nums[idx]; if (dfs(nums, idx + 1, edge, sums))return true; sums[i] -= nums[idx]; } } return false; } public: bool makesquare(vector<int>& nums) { int n = nums.size(); if (n < 4)return false; sort(nums.begin(), nums.end(),greater<int>()); // (1) int sum = 0; for (int i = 0; i < n; ++i)sum += nums[i]; if (sum % 4 != 0)return false; vector<int> sums = { 0,0,0,0 }; return dfs(nums, 0, sum / 4, sums); } }; [/cpp] 本代码提交AC,用时6MS。 第(1)个剪枝策略的含义是先对所有火柴棒的长度从大到小排序,为什么从大到小排序呢,因为我们的dfs过程中的for循环总是首先尝试把每根火柴棒放到第一个组份中,如果本身无解,则从小到大的策略会慢慢累积和,直到最后加上很长的火柴棒才会发现不满足if语句无解;而如果从大到小排序的话,一开始的累加和就很大,如果无解,则很快能发现不满足if。 第(2)个剪枝策略的含义是只有当把这根火柴棒加到这个组份中不会超过预定的edge,才把它加入,然后递归。这个很显然的。 第(3)个剪枝策略参考讨论区,如果递归的时候发现某一个组份当前的和等于之前某个组份的和,因为之前那个组份已经递归过了,所以本质上这次递归会和上次递归一样,可以continue掉。]]>

LeetCode Valid Square

LeetCode Valid Square Given the coordinates of four points in 2D space, return whether the four points could construct a square. The coordinate (x,y) of a point is represented by an integer array with two integers. Example:

Input: p1 = [0,0], p2 = [1,1], p3 = [1,0], p4 = [0,1]
Output: True
Note:
  1. All the input integers are in the range [-10000, 10000].
  2. A valid square has four equal sides with positive length and four equal angles (90-degree angles).
  3. Input points have no order.

任给平面上的四个点,问这四个点能否构成正方形。 解法1,原始几何角度。 如果要构成正方形,必须满足四条边相等,且四个角是直角。因为给定的点的顺序是不固定的,假设我们固定一个点p1,求p1到其他三个点p2,p3,p4的距离的平方len2,len3,len4,则要形成正方形,这三个距离中必定有两个距离相等,且等于另一个距离的两倍。
p3------p4
|       |
|       |
p1------p2
比如假设形成上述正方形,则有2*len2==2*len3==len4。在满足这个条件的基础上,还需要满足p1p2垂直p1p3,且p4p3垂直p4p2,且p4到p2和p3的距离相等。 当然因为点的顺序不固定,所以还有可能2*len2==2*len4==len3或者2*len3==2*len4==len2。最后需要注意的是这些点中不能由任何两个点重合,所以需要判断两点之间的距离不能为0。 完整代码如下: [cpp] class Solution2 { private: // 计算距离的平方 int distSquare(const vector<int>& p1, const vector<int>& p2) { return (p1[0] – p2[0])*(p1[0] – p2[0]) + (p1[1] – p2[1])*(p1[1] – p2[1]); } // 判断直线(p1,p2)和直线(p3,p4)是否垂直 bool isVertical(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) { return (p2[0] – p1[0])*(p4[0] – p3[0]) + (p2[1] – p1[1])*(p4[1] – p3[1]) == 0; } // 在2|p1p2|==2|p1p3|==|p1p4|的条件下,判断是否能形成正方形 bool helper(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) { if (!isVertical(p1, p2, p1, p3))return false; if (!isVertical(p4, p3, p4, p2))return false; int len2 = distSquare(p4, p2), len3 = distSquare(p4, p3); return len2 == len3; } public: bool validSquare(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) { int len2 = distSquare(p1, p2), len3 = distSquare(p1, p3), len4 = distSquare(p1, p4); if (len2 == 0 || len3 == 0 || len4 == 0)return false; if (len2 == len3 && 2 * len2 == len4) return helper(p1, p2, p3, p4); if (len2 == len4 && 2 * len2 == len3) return helper(p1, p2, p4, p3); if (len3 == len4 && 2 * len3 == len2) return helper(p1, p3, p4, p2); return false; } }; [/cpp] 本代码提交AC,用时6MS。 解法2,特征法。 任何一个正方形,如果求出所有点之间的距离,则这些距离只可能有两种取值,一种是邻边全相等,另一种是对角边也相等。所以我们只需要用一个map保存不同边的长度以及出现的频率即可,如果最终只有两个映射,则是正方形,否则不是。注意当出现边长为0时,说明两点重合,不能构成正方形。 代码如下: [cpp] class Solution { private: // 计算距离的平方 int distSquare(const vector<int>& p1, const vector<int>& p2) { return (p1[0] – p2[0])*(p1[0] – p2[0]) + (p1[1] – p2[1])*(p1[1] – p2[1]); } public: bool validSquare(vector<int>& p1, vector<int>& p2, vector<int>& p3, vector<int>& p4) { unordered_map<int, int> umdist; vector<vector<int>> points = { p1,p2,p3,p4 }; for (int i = 0; i < 4; ++i) { for (int j = i + 1; j < 4; ++j) { int dist = distSquare(points[i], points[j]); if (dist == 0)return false; ++umdist[dist]; } } return umdist.size() == 2; } }; [/cpp] 本代码提交AC,用时9MS。 不过上述代码有局限性,如果顶点都只是整数,则没有问题,但是如果顶点可以是实数,则会有问题。比如等边三角形的三个点和三角形的中心点,这四个点两两之间只有2种不同的距离,但他们不能构成正方形。可以再加一个限制条件,即两种不同长度的边的频率一个是4,一个是2,且出现2次的边长是出现4次的边长的两倍。 讨论区见:https://discuss.leetcode.com/topic/89985/c-3-lines-unordered_set/4]]>

LeetCode Longest Repeating Character Replacement

LeetCode Longest Repeating Character Replacement Given a string that consists of only uppercase English letters, you can replace any letter in the string with another letter at most k times. Find the length of a longest substring containing all repeating letters you can get after performing the above operations. Note: Both the string’s length and k will not exceed 104. Example 1:

Input:
s = "ABAB", k = 2
Output:
4
Explanation:
Replace the two 'A's with two 'B's or vice versa.
Example 2:
Input:
s = "AABABBA", k = 1
Output:
4
Explanation:
Replace the one 'A' in the middle with 'B' and form "AABBBBA".
The substring "BBBB" has the longest repeating letters, which is 4.

给定一个字符串s,可以把s中最多k个字符换成另一个字符,问这样做之后,最大能得到多长的重复字符的字符串。 比如样例一s=”ABAB”,k=2,把两个A都改为B之后,所有字符都变成了B,长度为4。 本题使用滑动窗口来解。每次我们统计窗口内出现频率最多的字符,那么最好的办法就是把窗口内其他字符全换成最高频的字符,条件就是其他字符的数量要不超过k,这样替换的次数才不会超过k。 在滑动窗口的时候,如果其他字符的数量超过k了,则需要缩减窗口大小,也就是把窗口左端往右滑。 代码如下: [cpp] class Solution { public: int characterReplacement(string s, int k) { int ans = 0, n = s.size(); vector<int> count(26, 0); int start = 0; for (int end = 0; end < n; ++end) { ++count[s[end] – ‘A’]; while (end – start + 1 – *max_element(count.begin(), count.end()) > k) { –count[s[start++] – ‘A’]; } ans = max(ans, end – start + 1); } return ans; } }; [/cpp] 本代码提交AC,用时29MS。渐进复杂度应该是O(n^2)的。 讨论区还有O(n)的方法,但是我看了好久都没理解,为啥max不会减少,为啥返回的直接是s.length()-start。]]>

LeetCode Combination Sum IV

LeetCode Combination Sum IV Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target. Example:

nums = [1, 2, 3]
target = 4
The possible combination ways are:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
Note that different sequences are counted as different combinations.
Therefore the output is 7.
Follow up: What if negative numbers are allowed in the given array? How does it change the problem? What limitation we need to add to the question to allow negative numbers?
给定一个数组,问从数组中有放回的取若干个数字,加起来等于target的方案数有多少种。 一开始以为是和LeetCode Combination Sum是类似的,只不过数字可以重复取而已,于是快速写出了如下的DFS版本: [cpp] class Solution { private: void dfs(vector<int>& nums, int sum, int& ans) { if (sum == 0) { ++ans; return; } if (sum < 0)return; for (const auto &i : nums) { if (sum >= i) { dfs(nums, sum – i, ans); } } } public: int combinationSum4(vector<int>& nums, int target) { int ans = 0; dfs(nums, target, ans); return ans; } }; [/cpp] 悲剧的是TLE了,给了一个样例[1,2,3],target=32,结果有181997601种之多,DFS确实吃不消。 究其原因还是因为每个数可以重复取值,如果每个数最多取一次,也就相当于0/1背包,每个数可以取或者不取,则用DP很好办: [cpp] class Solution { public: int combinationSum4(vector<int>& nums, int target) { vector<int> dp(target + 1, 0); dp[0] = 1; for (int i = 0; i < nums.size(); ++i) { // 对每个数字 for (int j = target; j >= nums[i]; –j) { dp[j] += dp[j – nums[i]]; // 取值;不取dp[j]保持不变;所以取和不取加起来就是+=dp[j-nums[i]] } } return dp[target]; } }; [/cpp] 但是这题中,每个数是可以重复取值的。因为数组中的数最小是1,所以每个数最多可以取target次,所以我们可以把0/1背包改成最多取target次的背包问题,也就是对于每个商品,我们可以尝试取1次、2次…target次。 我们可以换一种思路,假设dp[i-1]表示生成和为i-1的所有组合数,那么生成和为i的所有组合数等于所有生成和为i-nums[j]的组合数的和,即所有dp[i-nums[j]]的和。dp[i-nums[j]]表示减去nums[j]此时的组合数。 代码如下: [cpp] class Solution { public: int combinationSum4(vector<int>& nums, int target) { vector<int> dp(target + 1, 0); dp[0] = 1; for (int i = 1; i <= target; ++i) { for (int j = 0; j < nums.size(); ++j) { if (i >= nums[j])dp[i] += dp[i – nums[j]]; } } return dp[target]; } }; [/cpp] 本代码提交AC,用时3MS。仔细观察这个版本的代码和上个版本的代码,发现其实就是两层循环换了一下位置,前者求不放回的方案数,后者求放回的方案数。 如果有负数,则要限制每个数的使用次数了,因为如果有数1和-1,要生成和为0的组合数,则可以有无限种,比如[1,-1],[1,1,-1,-1]。]]>

LeetCode Longest Palindromic Subsequence

LeetCode Longest Palindromic Subsequence Given a string s, find the longest palindromic subsequence’s length in s. You may assume that the maximum length of s is 1000. Example 1: Input:

"bbbab"
Output:
4
One possible longest palindromic subsequence is “bbbb”. Example 2: Input:
"cbbd"
Output:
2
One possible longest palindromic subsequence is “bb”.
求一个字符串的最长回文子序列。 使用DP求解,假设dp[i][j]表示原字符串范围[i,j]中的最长回文子序列的长度。则如果s[i]==s[j],则dp[i][j]=dp[i+1][j-1]+2,即是临近内层的最长回文子序列长度加2。如果s[i]!=s[j],则dp[i][j]=max(dp[i][j-1],dp[i+1][j]),即是左端或者右端最长回文子序列长度的最大值。 初始时dp[i][i]=1,即单个字符自成回文,长度为1。 代码如下: [cpp] class Solution2 { public: int longestPalindromeSubseq(string s) { int n = s.size(); vector<vector<int>> dp(n, vector<int>(n, 0)); for (int i = n – 1; i >= 0; –i) { dp[i][i] = 1; for (int j = i + 1; j < n; ++j) { if (s[i] == s[j])dp[i][j] = dp[i + 1][j – 1] + 2; else dp[i][j] = max(dp[i][j – 1], dp[i + 1][j]); } } return dp[0][n – 1]; } }; [/cpp] 本代码提交AC,用时68MS。 上述计算有些区间会重复计算dp[i][j],可以用递归方法求解,并且把计算过的值记录下来,下次遇到查询时直接返回。代码如下: [cpp] class Solution { private: int helper(int l, int r, const string &s, vector<vector<int>> &dp) { if (l > r)return 0; if (l == r)return 1; if (dp[l][r] != 0)return dp[l][r]; if (s[l] == s[r])dp[l][r] = helper(l + 1, r – 1, s, dp) + 2; else dp[l][r] = max(helper(l, r – 1, s, dp), helper(l + 1, r, s, dp)); return dp[l][r]; } public: int longestPalindromeSubseq(string s) { int n = s.size(); vector<vector<int>> dp(n, vector<int>(n, 0)); return helper(0, n – 1, s, dp); } }; [/cpp] 本代码提交AC,用时49MS。 参考:https://discuss.leetcode.com/topic/78603/straight-forward-java-dp-solution/2]]>

LeetCode Shortest Unsorted Continuous Subarray

LeetCode Shortest Unsorted Continuous Subarray Given an integer array, you need to find one continuous subarray that if you only sort this subarray in ascending order, then the whole array will be sorted in ascending order, too. You need to find the shortest such subarray and output its length. Example 1:

Input: [2, 6, 4, 8, 10, 9, 15]
Output: 5
Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order.
Note:
  1. Then length of the input array is in range [1, 10,000].
  2. The input array may contain duplicates, so ascending order here means <=.

给定一个数组,问最短对多长的子数组排序之后,整个数组都会是升序的。比如样例对中间的[6,4,8,10,9]这个子数组排序之后,放回到原数组,原数组也是升序排列的。 第一种解法是,直接对数组排序,然后把已排序数组和未排序数组分别从首位对比,遇到第一个不相等的位置就跳出,然后需要排序的子数组长度就是j-i+1了。 代码如下: [cpp] class Solution { public: int findUnsortedSubarray(vector<int>& nums) { vector<int> nums_copy = nums; sort(nums_copy.begin(), nums_copy.end()); int i = 0, j = nums.size() – 1; while (i <= j&&nums[i] == nums_copy[i])++i; while (j >= i&&nums[j] == nums_copy[j])–j; return j – i + 1; } }; [/cpp] 本代码提交AC,用时52MS,时间复杂度是O(nlgn)。 还有一种O(n)的方法,参考这个题解。 基本思想是这样:
  1. 从左往右,如果该数已经在最终排序的位置了,那么该数必须小于等于该数右边的最小值
  2. 从右往左,如果该数已经在最终排序的位置了,那么该数必须大于等于该数左边的最大值
如果违反1,那么该数右边还有比该数小的数,所以需要把这个更小的数放到该数前面,也就是说该数不在其最终的位置;违反2也是类似的。 所以我们维护两个数组,分别存储从左往右的最大值和从右往左的最小值,然后和原数组比较即可。 代码如下: [cpp] /** * /————\ * nums: [2, 6, 4, 8, 10, 9, 15] * minsf: 2 4 4 8 9 9 15 * <——————– * maxsf: 2 6 6 8 10 10 15 * ——————–> */ class Solution { public: int findUnsortedSubarray(vector<int>& nums) { int n = nums.size(); vector<int> min_so_far(n, 0), max_so_far(n, 0); for (int i = 0, maxsf = INT_MIN; i < n; ++i)max_so_far[i] = maxsf = max(maxsf, nums[i]); for (int i = n – 1, minsf = INT_MAX; i >= 0; –i)min_so_far[i] = minsf = min(minsf, nums[i]); int i = 0, j = n – 1; while (i <= j&&nums[i] <= min_so_far[i])++i; while (j >= i&&nums[j] >= max_so_far[j])–j; return j – i + 1; } }; [/cpp] 本代码提交AC,用时72MS,时间复杂度是O(n),但是为啥比第一种方法还慢呀。]]>

LeetCode Longest Harmonious Subsequence

LeetCode Longest Harmonious Subsequence We define a harmonious array is an array where the difference between its maximum value and its minimum value is exactly 1. Now, given an integer array, you need to find the length of its longest harmonious subsequence among all its possible subsequences. Example 1:

Input: [1,3,2,2,5,2,3,7]
Output: 5
Explanation: The longest harmonious subsequence is [3,2,2,2,3].
Note: The length of the input array will not exceed 20,000.
一个和谐的数组是该数组的最大值和最小值只相差1。给定一个数组,求该数组的和谐子序列的最大长度。 子序列问题,又有作差问题,开始还以为是用DP来解,想了半天没想出好的解法。 后来看了一下标签用hash来做,恍然大悟。 仔细理解一下这个和谐数组的定义,最大数和最小数只相差1,说明这个子序列肯定只有两个不同的数,而且这两个数只相差1。 这就好办了,我们用hash表统计一下数组中每个数出现的频率,然后对于每个不同的数,看看该数相邻的数是否在hash表中,如果在的话,从数组中提取出这两个相邻的数的所有出现,构成的子序列就是一个和谐子序列。不断更新和谐子序列的最大长度。注意,假如考虑3构成的和谐子序列,则另一个数可能是2或者4,但是如果2在原数组中的话,已经考虑2和3构成和谐子序列的情况了,所以对于每个数,我们统一只考虑比它大1的数即可。 完整代码如下: [cpp] class Solution { public: int findLHS(vector<int>& nums) { unordered_map<int, int> hash; for (const auto &i : nums)++hash[i]; int ans = 0; for (const auto &it : hash) { if (hash.find(it.first + 1) != hash.end()) { ans = max(it.second + hash[it.first + 1], ans); } } return ans; } }; [/cpp] 本代码提交AC,用时102MS。]]>