1. 程式人生 > >演算法作業第二週(leetCode)——321. Create Maximum Number

演算法作業第二週(leetCode)——321. Create Maximum Number

     我個人感覺本次做的題比上次那道hard難多了,以下為題目地址:

     這道題的大意是給定兩個由各位數字(0-9)組成的陣列,用陣列中的數組合出一個最大的k位數。其中,數字在陣列中的相對位置不能改變。

      我一開始看到這道題的時候,心裡想的是直接用dfs,然而這道題用dfs可以讓本題演算法複雜度爆炸。然後,我想了一下,覺得動態規劃也不太可行,因為這道題前面的數字可以直接確定,就不存在多個狀態。題目沒有規定演算法複雜度是多少,我就想,這道題可不可以實現O(m+n)級別的演算法。

       後來,我想出了一種預處理辦法,就是通過一遍遍歷,儲存下來陣列中每個位置上下一個指定數字(0-9)所在的位置。這個只需要維護一個數組,表明指定數字上一次出現的位置,演算法複雜度為O(m+n)。然後,很明顯的是,最大的數前面的位數越大越好,於是可以很容易地想到貪心演算法,就是取兩個陣列中最大且最靠前的數,這裡需要注意的是,取了數之後必須保證還可以取接下來的數保證可以組成k位數字,否則就不能取。

     然後對於這道題,我想出了兩種思路。一是直接通過上述的預處理,在兩個陣列中直接取下一個最大最前的數。另一個想法是,將k分為兩部分,對每一個數組求最大序列,然後歸併合起來。然而,我當時想能不能實現O(m+n)的演算法,於是我就試了一下第一種演算法,結果後來發現跟想象的有些差距,改來改去超時了。。。。。

失敗的演算法:

         然後去看了一眼題解,發現別人的想法跟我第二種想法是一樣的,就是把k分為兩部分。區別在於,大神用了棧來處理單一陣列,而我是通過先將陣列預處理。感覺我還是很久沒有用過棧,現在水平下降較多。。。。。

然後根據大佬的演算法寫出了改進的演算法:

class Solution {
public:
    vector<int> maxArray(vector<int>& nums1, int k){
        int i,j,s1, temp, cnt;
        int last[10];
        vector<int> nextn[10], ans;
        s1=nums1.size();
        memset(last,-1,sizeof(last));
        for(i=0;i<s1;i++)
        {
            temp=nums1[i];
            if(last[temp]==-1)
            {
                nextn[temp].push_back(i);
                last[temp]=0;
            }
            for(j=last[temp];j<i;j++)
                nextn[temp].push_back(i);
            last[temp]=i;
        }
        cnt=-1;
        for(i=0;i<k;i++)
        {
            for(j=9;j>=0;j--)
            {
                if(cnt>=(int)nextn[j].size()-1||nextn[j].size()==0)
                    continue;
                temp=nextn[j][cnt+1];
                if(s1-temp-1<k-i-1)
                    continue;
                cnt=temp;
                ans.push_back(nums1[cnt]);
                break;
            }
        }
        return ans;
    }

    bool greater(vector<int>& nums1, int i, vector<int>& nums2, int j){
        while (i < nums1.size() && j < nums2.size() && nums1[i] == nums2[j]){
            i++;
            j++;
        }
        return j == nums2.size() || (i<nums1.size() && nums1[i] > nums2[j]);
    }

    vector<int> merge(vector<int>& nums1, vector<int>& nums2, int k) {
        std::vector<int> ans(k);
        int i = 0, j = 0;
        for (int r = 0; r<k; r++){
            if( greater(nums1, i, nums2, j) ) ans[r] = nums1[i++] ;
            else ans[r] = nums2[j++];
        }
        return ans;
    }

    vector<int> maxNumber(vector<int>& nums1, vector<int>& nums2, int k) {
        int m = nums1.size();
        int n = nums2.size();
        vector<int> result(k);
        for (int i = std::max(0 , k - n); i <= k && i <= m; i++){
            auto v1 = maxArray(nums1, i);
            auto v2 = maxArray(nums2, k - i);
            vector<int> candidate = merge(v1, v2, k);
            if (greater(candidate, 0, result, 0)) result = candidate;
        }
        return result;
    }

};

但是感覺還是比大佬演算法慢了好多(巨慢):

不過還是受益匪淺的,至少對棧的使用,還有預處理更加的熟悉了。 還是要好好學習呀!