321、拼接最大的数字

  1. 321、拼接最大的数字
    1. 示例 1:
    2. 示例 2:
    3. 示例 3:
  • 题解
    1. 1、数组的最大子序列+数组归并
  • 321、拼接最大的数字

    给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。

    求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。

    说明: 请尽可能地优化你算法的时间和空间复杂度。

    示例 1:

    输入:
    nums1 = [3, 4, 6, 5]
    nums2 = [9, 1, 2, 5, 8, 3]
    k = 5
    输出:
    [9, 8, 6, 5, 3]
    

    示例 2:

    输入:
    nums1 = [6, 7]
    nums2 = [6, 0, 4]
    k = 5
    输出:
    [6, 7, 6, 0, 4]
    

    示例 3:

    输入:
    nums1 = [3, 9]
    nums2 = [8, 9]
    k = 3
    输出:
    [9, 8, 9]
    

    题解

    1、数组的最大子序列+数组归并

    • 从所有的情况中选择最优:
      • 实现快速查找一个数组的最大子序列
      • 实现快速合并并两个数组,并保证序列的字典顺序最大
      • 优化时间复杂度
        $$
        [1…k] = max(\sum_{i=0}^k merge(subsequence(num1,i),subsequence(num2,k-i)))
        $$
    • 代码
    // java
    class Solution {
        public int[] maxNumber(int[] num1,int[] num2,int k) {
            int[] res = null;
            int len1 = num1.length;
            int len2 = num2.length;
            System.out.println("num1: ");
            print(num1);
            System.out.println("num2: ");
            print(num2);
            boolean first = true;
            for (int i = Math.max(0,k-len2);i <= Math.min(k,len1);i++) {
                int[] sub1 = subSequence(num1,i);
                System.out.println("sub1-"+i+": ");
                print(sub1);
                int[] sub2 = subSequence(num2,k-i);
                System.out.println("sub2-"+i+": ");
                print(sub2);
                int[] tmp = merge(sub1,sub2);
                System.out.println("tmp: ");
                print(tmp);
                if (first) {
                    res = tmp;
                    first = false;
                }else {
                    res = maxSubSequence(res,tmp)?res:tmp;
                }
                System.out.println("res: ");
                print(res);
            }
            return res;
        }
    
        private int[] subSequence(int[] nums,int k) {
            int l = nums.length;
            if (l <= k) return nums;
    
            int drop = l - k;
    
            int[] ans = new int[k];
            if (k == 0) return ans;
            Stack<Integer> stack = new Stack<>();
            for (int i = 0; i < l; i++) {
                while (!stack.empty() && nums[i] > stack.peek() && drop-- > 0) {
                    stack.pop();
                }
                stack.push(nums[i]);
            }
            // 裁剪大小
            while (stack.size() > k) stack.pop();
            
            for (int i = k - 1;i >=0 ;i--) {
                ans[i] = stack.pop();
            }
            return ans;
        }
    
        private void print(int[] nums) {
            for (int item : nums) {
                System.out.print(item+" ");
            }
            System.out.println();
        } 
    
        // 有问题
        private int[] merge(int[] nums1,int[] nums2) {
            int l1 = nums1.length;
            int l2 = nums2.length;
            if(l1==0) return nums2;
            if(l2==0) return nums1;
            int ans[] = new int[l1+l2];
            int i1 = 0;
            int i2 = 0;
            for(int i=0;i<l1+l2;i++){
                if(maxSubSequence(Arrays.copyOfRange(nums1, i1, l1),Arrays.copyOfRange(nums2, i2, l2))){
                    ans[i] = nums1[i1++];
                }
                else{
                    ans[i] = nums2[i2++];
                }
            }
            return ans;
        }
    
        // true -> nums1 > nums2
        private boolean maxSubSequence(int[] nums1,int[] nums2) {
            // 求出长度较小的值
            int n = Math.min(nums1.length,nums2.length);
            for(int i=0;i < n;i++){
                if(nums1[i] > nums2[i]) return true;
                else if(nums1[i] < nums2[i]) return false;
                else continue;
            }
            return nums1.length > nums2.length;
        }
        public static void main(String[] args) {
            Leetcode321 leetcode321 = new Leetcode321();
            int[] num1 = {3,4,6,5};
            int[] num2 = {9, 1, 2, 5, 8, 3};
            leetcode321.maxNumber(num1, num2, 5);
            System.out.println("-------------------------------");
            int[] num3 = {6,7};
            int[] num4 = {6,0,4};
            leetcode321.maxNumber(num3, num4, 5);
        }
    }
    

    -

    class Solution {
        vector<int> nums1;// 队列1
        vector<int> nums2;// 队列2
    
        int findMaxIndex(vector<int> &nums, int idx, int len){
            int maxIdx = idx;
            for (int i = idx; i < nums.size() - len; ++i){
                if (nums[i] > nums[maxIdx]){
                    maxIdx = i;
                }
            }
            return maxIdx;
        }
    
        // 重复计算太多,时间复杂度太高 
        void dfs(vector<int> &rst, int idx1, int idx2, int k){
            if (k == 0){
                return;
            }
    
            int len1 = max(0, k - ((int)nums2.size() - idx2)-1);
            int maxIdx1 = findMaxIndex(nums1, idx1, len1);
            // printf("idx1 = %d, len1=%d, maxIdx1=%d\n", idx1, len1, maxIdx1);
    
            int len2 = max(0, k - ((int)nums1.size() - idx1)-1);
            int maxIdx2 = findMaxIndex(nums2, idx2, len2);
            // printf("idx2 = %d, len2=%d, maxIdx2=%d\n", idx2, len2, maxIdx2);
    
            if (maxIdx1 == nums1.size()){
                rst.push_back(nums2[maxIdx2]);
                return dfs(rst, idx1, maxIdx2 + 1, k-1);
            }
            else if (maxIdx2 == nums2.size()){
                rst.push_back(nums1[maxIdx1]);
                return dfs(rst, maxIdx1 + 1, idx2, k-1);
            }
            else{
                if (nums1[maxIdx1] > nums2[maxIdx2]){
                    rst.push_back(nums1[maxIdx1]);
                    return dfs(rst, maxIdx1 + 1, idx2, k-1);
                }
                else if (nums1[maxIdx1] < nums2[maxIdx2]){
                    rst.push_back(nums2[maxIdx2]);
                    return dfs(rst, idx1, maxIdx2 + 1, k-1);
                }
                else {
                    rst.push_back(nums1[maxIdx1]);
                    vector<int> rst1, rst2;
                    rst1 = rst2 = rst;
                    dfs(rst1, maxIdx1 + 1, idx2, k-1);
                    dfs(rst2, idx1, maxIdx2 + 1, k-1);
    
                    rst = rst1;
                    for (int i = 0; i < rst1.size(); ++i){
                        if (rst1[i] > rst2[i]){
                            break;
                        }
                        else if (rst1[i] < rst2[i]){
                            rst = rst2;
                            break;
                        }
                    }
                    return;
                }
            }
        }
    
        // 广度优先搜索
        void bfs(vector<int> &rst, int k){
            set<pair<int, int>> s1;        
            s1.insert(make_pair(0, 0));
    
            while (k){
                int maxNum = -1;
                set<pair<int, int>> s2;
                for (auto &p : s1){// 遍历s1
                    int idx1 = p.first;
                    int idx2 = p.second;
    
                    int len1 = max(0, k - ((int)nums2.size() - idx2)-1);
                    int maxIdx1 = findMaxIndex(nums1, idx1, len1);// 找到最大值的下标
                    int maxNum1 = maxIdx1 >= nums1.size() ? -1 : nums1[maxIdx1];
                    // printf("idx1 = %d, len1=%d, maxIdx1=%d, maxNum1=%d\n", idx1, len1, maxIdx1, maxNum1);
    
                    if (maxNum1 > maxNum){
                        s2.clear();
                        s2.insert(make_pair(maxIdx1 + 1, idx2));
                        maxNum = maxNum1;
                    }
                    else if (maxNum1 == maxNum){
                        s2.insert(make_pair(maxIdx1 + 1, idx2));
                    }
    
                    int len2 = max(0, k - ((int)nums1.size() - idx1)-1);
                    int maxIdx2 = findMaxIndex(nums2, idx2, len2);
                    int maxNum2 = maxIdx2 >= nums2.size() ? -1 : nums2[maxIdx2];
                    // printf(" idx2 = %d, len2=%d, maxIdx2=%d, maxNum2=%d\n", idx2, len2, maxIdx2, maxNum2);
    
                    if (maxNum2 > maxNum){
                        s2.clear();
                        s2.insert(make_pair(idx1, maxIdx2 + 1));
                        maxNum = maxNum2;
                    }
                    else if (maxNum2 == maxNum){
                        s2.insert(make_pair(idx1, maxIdx2 + 1));
                    }
                }
                s1.swap(s2);
                rst.push_back(maxNum);
                --k;
            }
        }    
    
    public:
        vector<int> maxNumber(vector<int>& a, vector<int>& b, int k) {
            nums1.swap(a);
            nums2.swap(b);
    
            vector<int> rst;
            //dfs(rst, 0, 0, k);
            bfs(rst, k);
            return rst;
        }
    };
    
    // 作者:hareast
    // 链接:https://leetcode-cn.com/problems/create-maximum-number/solution/zheng-chang-si-lu-qiu-jie-bfs28-ms-zai-suo-you-cpp/
    // 来源:力扣(LeetCode)
    // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    class Solution {
    public:
        vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
            vector<int>ans;
            const int n1=nums1.size();
            const int n2=nums2.size();
            
            // 迭代求最优解
            for(int k1=0;k1<=k;++k1)
            {
                const int k2=k-k1;
                if(k1>n1||k2>n2)
                    continue;
                ans=max(ans,maxNumber(maxNumber(nums1,k1),maxNumber(nums2,k2)));
            }
            
            return ans;
        }
        
    private:
        // 求数组nums的大小为k的最大自序列
        vector<int>maxNumber(vector<int>&nums, int k)
        {
            if(k==0) return{};
            vector<int>ans;// 利用栈
            int to_pop=nums.size()-k;
            for(const int num:nums)
            {
                // 删除小的数字
                while(!ans.empty()&&num>ans.back()&&to_pop-->0)
                    ans.pop_back();
                ans.push_back(num);// 添加大的数字或者添加小的数字
            }
            ans.resize(k);// 重新设置大小
            return ans;
        }
        vector<int>maxNumber(const vector<int>&nums1,const vector<int>&nums2)
        {
            vector<int>ans;
            auto s1=nums1.cbegin();
            auto s2=nums2.cbegin();
            auto e1=nums1.cend();
            auto e2=nums2.cend();
            // 合并两个子数组,调用库函数lexicographical_compare
            while(s1!=e1||s2!=e2)
                ans.push_back(
                    lexicographical_compare(s1,e1,s2,e2)? *s2++: *s1++);
            return ans;
        }
        
    };
    
    
    // 作者:easier
    // 链接:https://leetcode-cn.com/problems/create-maximum-number/solution/ba-wen-ti-fen-jie-cheng-zi-wen-ti-qiu-jie-cjie-fa-/
    // 来源:力扣(LeetCode)
    // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    class Solution {
    public:
        vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
            vector<int> res(k, 0);
            int n = nums1.size(), m = nums2.size();
            // 假设有最大子序列中有s个元素来自nums1,对所有可能的s值遍历
            for (int s=max(0, k-m); s<=min(k, n); s++){
                vector<int> temp;
                int i = 0, j = 0;
                // nums1中长度为s的最大子序列
                vector<int> temp1 = maxKsequence(nums1, s);
                // nums2中长度为k-s的最大子序列
                vector<int> temp2 = maxKsequence(nums2, k-s);
                // 对两个子序列进行归并
                // lexicographical_compare:比较两个序列的字典序大小
                auto iter1 = temp1.begin(), iter2 = temp2.begin();
                while (iter1 != temp1.end() || iter2 != temp2.end()){
                    temp.push_back(lexicographical_compare(iter1, temp1.end(), iter2, temp2.end()) ? *iter2++ : *iter1++);
                }
                // 如果归并后的最大子序列大于目前已找到的最大子序列,则更新解
                res = lexicographical_compare(res.begin(), res.end(), temp.begin(), temp.end()) ? temp : res;
            }
            return res;
        }
    
        // 求数组v的长度为k的最大子序列
        vector<int> maxKsequence(vector<int> v, int k){
            int n = v.size();
            if (n <= k)
                return v;
            vector<int> res;
            int pop = n-k;
            for (int i=0; i<n; i++){
                while(!res.empty() && v[i]>res.back() && pop-->0)
                    res.pop_back();
                res.push_back(v[i]);
            }
            res.resize(k);
            return res;
        }
    };
    
    // 作者:Gorilla
    // 链接:https://leetcode-cn.com/problems/create-maximum-number/solution/cshou-xian-qiu-jie-zi-wen-ti-zai-he-bing-zi-wen-ti/
    // 来源:力扣(LeetCode)
    // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • Java实现
    class Solution {
        public int[] maxNumber(int[] nums1, int[] nums2, int k) {
            int m = nums1.length;
            int n = nums2.length;
            int[] ans = new int[k];
    
            for(int i=Math.max(0,k-n);i<=Math.min(k,m);i++){
                int[] seq1 = maxSubSequence(nums1,i);    //子序列1
                int[] seq2 = maxSubSequence(nums2,k-i);  //子序列2
                int[] temp = merge(seq1,seq2);           //归并
                if(compare(temp,ans)){                   //比较逻辑大小
                    // for(int j=0;j<k;j++){
                    //     ans[j] = temp[j];
                    // }
                    ans = temp;
                }
            }
            return ans;
        }
    
        //求数组中k个顺序不变的最大子序列
        private int[] maxSubSequence(int[] nums, int k){
            int l = nums.length;
            if(l<=k) return nums;
            
            //代表最多可以丢弃几个数
            int drop = l-k;
    
            int[] ans = new int[k];
            if(k==0) return ans;
            Stack<Integer> stack = new Stack<>();
            for(int i=0;i<l;i++){
                while(!stack.empty() && nums[i]>stack.peek() && drop-->0){
                    stack.pop();
                }
                stack.push(nums[i]);
                
            }
            //裁剪大小
            while(stack.size()>k) stack.pop();
    
            for(int i=k-1;i>=0;i--){
                ans[i] = stack.pop();
            }
            return ans;
        }
    
        //归并数组
        //这里遇到了好多坑,一开始是想着按照归并排序那种方式归并的,结果发现在遇到连续几位数字相同的情况下时会出现问题
        private int[] merge(int[] nums1, int[] nums2){
            int l1 = nums1.length;
            int l2 = nums2.length;
            if(l1==0) return nums2;
            if(l2==0) return nums1;
            int ans[] = new int[l1+l2];
            int i1 = 0;
            int i2 = 0;
            for(int i=0;i<l1+l2;i++){
                if(compare(Arrays.copyOfRange(nums1, i1, l1),Arrays.copyOfRange(nums2, i2, l2))){
                    ans[i] = nums1[i1++];
                }
                else{
                    ans[i] = nums2[i2++];
                }
            }
            return ans;
        }
    
        //比较数组的逻辑大小,如果数组长短不一样并且前n个数字完全一样,则认为长度大的那个数组大
        //返回值:若nums1>nums2,返回true
        private boolean compare(int[] nums1, int[] nums2){
            int n = Math.min(nums1.length,nums2.length);
            for(int i=0;i<n;i++){
                if(nums1[i]>nums2[i]) return true;
                else if(nums1[i]<nums2[i]) return false;
                else continue;
            }
            return nums1.length>nums2.length;
        }
        
    }
    
    // 作者:nju_yirui
    // 链接:https://leetcode-cn.com/problems/create-maximum-number/solution/javajie-fa-by-nju_yirui/
    // 来源:力扣(LeetCode)
    // 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
    • 5ms
    class Solution {
        private int[] res;
        public int[] maxNumber(int[] nums1, int[] nums2, int k) {
            res = new int[k];
            for(int i = 0; i <= k; i ++)
                if(i <= nums1.length && k - i <= nums2.length)
                // 获取最大的子序列并合并
                    merge(getMax(nums1, i), getMax(nums2, k - i));
            return res;
        }
        private void merge(int[] nums1, int[] nums2){
            int p = 0, q = 0, r = 0;
            boolean flag = false;
            while(p < nums1.length || q < nums2.length){
                int t = gt(nums1, p, nums2, q) ? nums1[p ++] : nums2[q ++];
                if(flag || (!flag && t > res[r])){
                    res[r] = t;
                    flag = true;
                }
                else if(!flag && t < res[r]) return;// 合并是发现结果小雨之间的结果,直接退出
                r ++;
            }
        }
        private boolean gt(int[] nums1, int p, int[] nums2, int q){
            while(p < nums1.length && q < nums2.length && nums1[p] == nums2[q]){
                p ++;
                q ++;
            }
            return q == nums2.length || (p < nums1.length && nums1[p] > nums2[q]);
        }
        private int[] getMax(int[] nums, int k){
            int[] res = new int[k];
            if(k == 0) return res;
    
            int n = nums.length, size = 0;
            for(int i = 0; i < n; i ++){
                // 用数组模拟栈
                while(size > 0 && n - i > k - size && nums[i] > res[size - 1])
                    size --;
                if(size < k) res[size ++] = nums[i];
            }
            return res;
        }
        
    }
    

    转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com

    💰

    Title:321、拼接最大的数字

    Count:2.9k

    Author:攀登

    Created At:2020-07-26, 00:19:44

    Updated At:2024-06-15, 15:52:32

    Url:http://jiafeimao-gjf.github.io/2020/07/26/321%E3%80%81%E6%8B%BC%E6%8E%A5%E6%9C%80%E5%A4%A7%E7%9A%84%E6%95%B0%E5%AD%97/

    Copyright: 'Attribution-non-commercial-shared in the same way 4.0' Reprint please keep the original link and author.

    ×

    Help us with donation