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. You should try to optimize your time and space complexity.
Example 1:
nums1 = [3, 4, 6, 5]
nums2 = [9, 1, 2, 5, 8, 3]
k = 5
return [9, 8, 6, 5, 3]
Example 2:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
return [6, 7, 6, 0, 4]
Example 3:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
return [9, 8, 9]
摘自:
https://discuss.leetcode.com/topic/32230/share-my-21ms-java-solution-with-comments
http://bookshadow.com/weblog/2015/12/24/leetcode-create-maximum-number/
http://blog.youkuaiyun.com/zly9923218/article/details/51014902
http://www.bubuko.com/infodetail-1278460.html
To find the maximum ,we can enumerate how digits we should get from nums1 , we suppose it is i.
So , the digits from nums2 is K - i.
And we can use a stack to get the get maximum number(x digits) from one array.
OK, Once we choose two maximum subarray , we should combine it to the answer.
It is just like merger sort, but we should pay attention to the case: the two digital are equal.
we should find the digits behind it to judge which digital we should choose now.
In other words,we should judge which subarry is bigger than the other.
That's all.
If you have any question or suggest, I am happy you can comment on my blog : Create Maximum Number.
Thanks, merry christmas
update:use stack to find max sub array and it runs 21ms now.( thanks to @dietpepsi )
---------------------------------------------------------------------------------------------------------------
问题可以转化为这样的两个子问题:
枚举nums1子数组与nums2子数组的长度len1, len2,在满足长度之和len1+len2等于k的前提下,分别求解最大子数组,并进行合并。
然后从合并得到的子数组中取最大数组即为所求。
子问题1的求解:
参考[LeetCode]Remove Duplicate Letters的思路,利用栈保存最大值子数组
时间复杂度为O(n),其中n为数组的长度。
子问题2的求解:
两数组的合并可以类比归并排序中的merge操作,只不过在选择两数组中较大的元素时,需要对数组剩余部分的元素进行比较,详见代码。
--------------------------------------------------------------------------------------------------
对i做循环
- 第一个数组中取出i个,第二个数组中取出k-i个
- 把i个和k-i个分别存储在两个栈中
- 对两个栈做归并
循环k-1次。
入栈的顺序:
首先检查当前数组元素和栈顶元素相对大小:
1、大于栈顶元素,把栈顶元素弹出
2、小于等于栈顶元素,直接入栈
3、如果栈length+数组剩余length==需要入栈的总数量,则全部入栈
比如9,1,9,8,3,2,需要入4个
9
1,9
9,9
8,9,9
3,8,9,9
最后出栈的是9,9,8,3
从第一个数组中取i个数字,组成最大的数,同理从第二个数组中取k - i个数字,合并起来之后结果也是最大的。
最外层循环从0到k,如果i和k - i超过了对应数组的长度,跳过这一轮。
然后分别从两个数组中取i位和k - i位。
从数组中取i位组成的数字最大,这个子问题类似于Remove Duplicate Letters的思路。
贪心,开一个栈,每次都尽量往里塞最大的数,最后这个栈就是结果。
假设需要取n位数字,每一轮入栈前要先维护栈的状态,如果同时满足以下条件就把栈顶的元素弹出:
1. 栈不为空;
2. 栈顶元素小于当前的元素;
3. 栈顶弹出后,后面剩下没处理的子串加上栈的length要够n位,
看这个例子[3,2,1,4,5],n = 3。
前三轮没有疑问,栈里放入3, 2, 1,stack = [3, 2, 1]。
第四轮循环的时候4比3, 2, 1都大,但不能把它们都弹出,因为不够3位了,只弹出2个,stack = [3, 4]。
最后5放入栈,stack = [3, 4, 5]。
第三点对应代码中 "stack.length + len - i > n" 这个条件。
分别取得了两个最大的子数,合并的操作就类似于merge sort。
双指针,一开始都指向0,放入一个大的,指针++,最后把剩下的都放入结果。
要注意可能有相同的数字,此时要看后面的数来决定哪个比较大,举例来说,[6, 1] 和 [6, 7]应该拿[6, 7]中的6。
比较其实可以不必逐位比较,直接把当前开始位置截取字串,利用字串的比较即可。
--------------------------------------------------------------------------------------------------------------------------------------
/** * Created by hrwhisper on 2015/11/23. * http://www.hrwhisper.me/leetcode-create-maximum-number/ */
public class Solution {
public int[] maxNumber(int[] nums1, int[] nums2, int k) {
int get_from_nums1 = Math.min(nums1.length, k);
int[] ans = new int[k];
for (int i = Math.max(k - nums2.length, 0); i <= get_from_nums1; i++) {
int[] res1 = new int[i];
int[] res2 = new int[k - i];
int[] res = new int[k];
res1 = solve(nums1, i);
res2 = solve(nums2, k - i);
int pos1 = 0, pos2 = 0, tpos = 0;
while (res1.length > 0 && res2.length > 0 && pos1 < res1.length && pos2 < res2.length) {
if (compare(res1, pos1, res2, pos2))
res[tpos++] = res1[pos1++];
else
res[tpos++] = res2[pos2++];
}
while (pos1 < res1.length)
res[tpos++] = res1[pos1++];
while (pos2 < res2.length)
res[tpos++] = res2[pos2++];
if (!compare(ans, 0, res, 0))
ans = res;
}
return ans;
}
public boolean compare(int[] nums1, int start1, int[] nums2, int start2) {
for (; start1 < nums1.length && start2 < nums2.length; start1++, start2++) {
if (nums1[start1] > nums2[start2]) return true;
if (nums1[start1] < nums2[start2]) return false;
}
return start1 != nums1.length;
}
public int[] solve(int[] nums, int k) {
int[] res = new int[k];
int len = 0;
for (int i = 0; i < nums.length; i++) {
while (len > 0 && len + nums.length - i > k && res[len - 1] < nums[i]) {
len--;
}
if (len < k)
res[len++] = nums[i];
}
return res;
} }
-------------------------------------------------------------------------------------------------
贴一个裸搜版本,指数复杂度,即使有剪枝,数据大了也跑不动。
String curmax="";
public int[] maxNumber(int[] nums1, int[] nums2, int k)
{
if(k==0)
return new int[]{};
int len1=nums1.length;
int len2=nums2.length;
if(len1>len2)
return maxnumaux(nums2, nums1, k);
return maxnumaux(nums1, nums2, k);
}
public int[] maxnumaux(int[] nums1, int[] nums2, int k)
{
int len1=nums1.length;
int len2=nums2.length;
String s="";
String max="";
for(int m=0;m<=k;m++)
if(m<=len1&&k-m<=len2)
{
String num1s=getmaxnum(nums1, m);
String num2s=getmaxnum(nums2, k-m);
if(num1s.length()==0)
s=num2s;
else if(num2s.length()==0)
s=num1s;
else
{
StringBuilder sb=new StringBuilder();
int pos1=0;
int pos2=0;
while(pos1<num1s.length()||pos2<num2s.length())
{
if(pos1==num1s.length())
sb.append(num2s.charAt(pos2++));
else if(pos2==num2s.length())
sb.append(num1s.charAt(pos1++));
else {
char c1=num1s.charAt(pos1);
char c2=num2s.charAt(pos2);
int cmp=c1-c2;
if(c1>c2)
{
sb.append(c1);
pos1++;
}
else if(c1<c2)
{
sb.append(c2);
pos2++;
}
else {
cmp=num1s.substring(pos1).compareTo(num2s.substring(pos2));
if(cmp>0)
{
sb.append(c1);
pos1++;
}
else if(cmp<0)
{
sb.append(c2);
pos2++;
}
else {
sb.append(c1);
sb.append(c2);
pos1++;
pos2++;
}
}
}
}
s=sb.toString();
}
if(max.compareTo(s)<0)
max=s;
}
s=max;
int[] retarr=new int[s.length()];
for(int i=0;i<s.length();i++)
retarr[i]=s.charAt(i)-'0';
return retarr;
}
public String getmaxnum(int[] arr,int k)
{
curmax="";
if(k>0)
dfs(arr, 0, k, -1, new StringBuilder(), false);
return curmax;
}
public void dfs(int[] num,int deep,int k,int lastpos,StringBuilder numpath, boolean lastinc)
{
if(deep==k)
{
String val=numpath.toString();
if(val.compareTo(curmax)>0)
curmax=val;
return ;
}
if(lastpos+1+k-deep>num.length)
return ;
for(int i=lastpos+1;i<num.length;i++)
if(curmax.length()==0||lastinc||num[i]>=(String.valueOf(curmax).charAt(deep)-'0'))
{
numpath.append(num[i]);
dfs(num, deep+1, k, i, numpath,true);
numpath.deleteCharAt(numpath.length()-1);
}
}