1. 程式人生 > >【LeetCode 321】Create Maximum Number

【LeetCode 321】Create Maximum Number

Given two arrays of length m and n with digits 0-9 representing two numbers. 
Create the maximum number of length k <= m + n from digits of the two. 
The relative order of the digits from the same array must be preserved. 
Return an array of the k digits.

Note: You should try to optimize your time and space complexity.

題意:給出兩個長度分別為m和n的陣列,它們的元素均為數字(0~9),請從中找出K個數字來
組成一個最大的K位數,要求來源於同一個陣列中元素在新的數裡的相對位置必須和原陣列一致。

Example 1:
Input:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
Output:
[9, 8, 6, 5, 3]

Example 2:
Input:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
Output:
[6, 7, 6, 0, 4]

Example 3:
Input:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
Output:
[9, 8, 9]

思路:從陣列1中找 i 個數字組成最大的i位數,從陣列2中找 K-i 個數字組成最大的K-i位數,然後將它們合併成最大的K位數。i從0到K遍歷一遍,從中找出最優的K位數。
問題劃分成三個部分:
1)從一個長度為N的陣列中抽取最大的包含 i 個元素的陣列;
2)將兩個長度分別為M和N的陣列 合併 成最大的一個數組;
3)i從0到K遍歷,找到最優的解。[ i 具體的最小值是 0 和 K-nums2.size() 之間的最大值,i 的最大值同理計算]

1/3 從長度為N的數字陣列中抽取最大的 K位數

如 [2,6,0,7,4,4,5,6 ]中最大的3位數是 756, 最大的7位數是 6074456. 為了解決該問題,一個較笨的辦法是遍歷K次,第一次取出陣列中的最大值(保證該最大值後面仍有充足元素可以取的前提下),下一次取該最大值後面部分陣列中的最大值... 但有一個時間複雜度更優的方法:單調棧。

把陣列中的元素依次壓入一個單調棧,壓入新元素時,若棧頂元素比它小就彈出再比較新的棧頂,直到遇到不比它小的元素,但同時需要注意棧裡的元素和剩餘陣列的元素的個數之和不能比K還小,否則就無法形成K位數了。

vector<int> getMax(vector<int>& nums, int k) {
	vector<int> stk;
	for (int i = 0; i < nums.size(); i++) {
		int numLeft = stk.size() + nums.size() - i;
		while ((!stk.empty())&& stk.back() < nums[i] && numLeft > k) {
			stk.pop_back();
			numLeft--;
		}
		stk.push_back(nums[i]);
	}
	stk.resize(k);//取前K位
	return stk;
}

2/3 將兩個陣列 合併 成一個數組 [難]

如果是兩個有序數組合並,那麼只需要用兩個下標號分別指向兩個陣列的開頭,然後比較下標處的元素大小,大的元素新增進新陣列並且下標號加一即可。注意迴圈結束後需要處理其中一個數組已經取完而另一個數組還沒有取完的情況。

但本部分要合併的兩個陣列是無序的,這時若下標處的元素相等時,必須考慮它後面的元素大小才能決定取哪一個。比如:
A[6,7]和B[6,0,4]合併,若先取B的6,則下一輪A的6比B的0大,繼續取6,依次得到[6,6,7,0,4],
若先取A的6,則下一輪A的7比B的6大,繼續取7,依次得到[6,7,6,0,4],這才是正確結果。

所以每一步都需要比較兩個下標之後剩餘陣列之間的大小,可以為此定義一個比較函式:

//比較下標ia之後的a陣列和下標ib之後的b陣列
bool cmp(vector<int>& a,int ia, vector<int>& b,int ib) {
	while(ia<a.size() && ib<b.size()) {
		if (a[ia] > b[ib]) return 1;
		else if (a[ia] < b[ib]) return 0;
		else
			ia++, ib++;
	}
	return ia<a.size();
}

然後是合併函式:

vector<int> merge(vector<int> nums1, vector<int> nums2) {
	int ind1 = 0, ind2 = 0;
	vector<int> ans;
	while (ind1 < nums1.size() && ind2 < nums2.size()) {
		if (cmp(nums1,ind1,nums2,ind2))
			ans.push_back(nums1[ind1++]);
		else
			ans.push_back(nums2[ind2++]);
	}
	while (ind1 < nums1.size()) ans.push_back(nums1[ind1++]);
	while (ind2 < nums2.size()) ans.push_back(nums2[ind2++]);
	return ans;
}

3/3 主函式 i從0到K迴圈,找出最優的K位數

vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
	vector<int> ans;
	for (int i = max(0, k - nums2.size()); i <= min(nums1.size(), k); i++) {
		vector<int> curAns = merge(getMax(nums1, i), getMax(nums2, k - i));
		if (cmp(curAns, 0,ans,0)) ans = curAns;
	}
	return ans;
}