[Leetcode]Dynamic Programming-note

Dynamic Programming

338. Counting Bits

[Desciption]

Counting Bits

[Analysis]

method-1

本題有非常簡單的做法,就是可以直接對每個數單獨計算其二進制下1的個數。

    vector<int> countBits(int num) {
        vector<int> vec(num + 1, 0);
        for (int i = 0; i <= num; i++) {
            int count = 0;
            int value = i;
            while (value) {
                count += value % 2;
                value >>= 1;
            }
            vec[i] = count;
        }
        return vec;
    }

但很顯然,這種做法下,時間複雜度爲O(n * sizeof(i))。

method-2

其實在計算每個數的二進制下1的個數時,存在着大量的重複。
例如111(7)的1的個數,就可以表示爲最後一位的1的個數+除最後一位外1的個數。也就是1 + 2。
因此,我們可以寫出代碼。

class Solution {
public:
    vector<int> countBits(int num) {
        vector<int> vec(num + 1, 0);
        for (int i = 1; i <= num; i++) {
            vec[i] = vec[i>>1] + i%2;
            cout << vec[i] << " " << vec[i >> 1] << endl;
        }
        return vec;
    }
};

此時的時間複雜度爲O(n)。

647. Palindromic Substrings

[Description]

647. Palindromic Substrings

[analysis]

method-1

最簡單而直觀的方法就是不採取動態規劃,直接遍歷字符串,尋找所有可能的迴文字符串。也就是說,用一個雙重循環遍歷字符串,可以得到該字符串所能構成的所有子字符串,然後對這些子字符串檢驗是否爲迴文串。

時間複雜度爲O( n^3 ),空間複雜度爲O(1)。

method-2

顯然,方法1存在很多重複計算的過程。這時我們可以通過動態規劃的方法來減少時間複雜度。在檢驗迴文字符串的時候,不一定要對所有的子字符串都進行檢驗。比如說,對於abccbs這樣的子字符串,顯然收尾已經不相同,不需要進行檢驗,又對於abccba,此時首尾相同,需要檢驗bccb是否爲迴文字符串,但顯然這個檢驗的過程已經在前面檢驗以b爲尾字符的子字符串中完成了,所以這時候我們就可以得到一種優化的方法。不妨構建一個矩陣,保存所有能夠構成迴文字符串的首index和尾index。此時同樣需要遍歷,但不需要O(n)的時間複雜度進行迴文字符串校驗了。

    int countSubstrings(string s) {
        int len = s.length();
        int count = 0;
        vector<vector<int>> matrix(len, vector<int>(len, 0));
        for (int i = 0; i < len; i++) {
            matrix[i][i] = 1;
        }
        for (int i = 0; i < len; i++) {
            count++;
            for (int j = 0; j < i; j++) {
                if (s[j] == s[i]) {
                    if (j == i - 1 || matrix[j + 1][i - 1] == 1) {
                        count++;
                        matrix[j][i] = 1;
                    }
                }
            }
        }
        return count;
    }

但需要明白的問題是,此時我們是以犧牲空間換時間,時間複雜度爲O( n^2 ),空間複雜度爲O( n^2 )。

method-3

實際上,還存在一種更好的方法。因爲對於任意一個子字符串,都只存在一個唯一的中間index(當子字符串長度爲奇數時,index爲單一值;當子字符串長度爲偶數時,index爲兩個值)。這時,我們便可以把外層循環表示爲遍歷所有子字符串的中間index,然後從這個中間index向兩邊拓展。在拓展的過程中校驗是否滿足迴文字符串。

    void extend(string s, int left, int right, int& count) {
        while(left >= 0 && right < s.length() && s[left] == s[right]){
            left--;
            right++;
            count++;
        }
    }

    int countSubstrings(string s) {
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            extend(s, i, i, count);
            extend(s, i, i + 1, count);
        }
        return count;
    }

此時,空間複雜度爲O(1),時間複雜度爲O( n^2 )。

646. Maximum Length of Pair Chain

[Description]

646. Maximum Length of Pair Chain

[Analysis]

method-1

這個題暴力求解應該是不那麼容易的。我首先想到的是用動態規劃的想法,用一個vector記錄以當前index爲pair chain最後一個元素的最大長度。通過對每一個index遍歷前面所有的數組,可以計算出對於以該index作爲最後一個chain元素的情況下,最大長度。但需要注意的是,題目沒有說已經排好序,所以需要自行排序。

    int findLongestChain(vector<vector<int>>& pairs) {
        sort(pairs.begin(), pairs.end(), [](vector<int> vec_1, vector<int> vec_2) {
            return vec_1[0] < vec_2[0];
        });
        int len = pairs.size();
        vector<int> vec(len, 1);
        for (int i = 1; i < len; i++) {
            int max = 1;
            for (int j = 0; j < i; j++) {
                if (pairs[i][0] > pairs[j][1]) {
                    if (vec[j] + 1 > max) {
                        max = vec[j] + 1;
                    } 
                }
            }
            vec[i] = max;
        }
        int max = 0;
        for (int i = 0; i < len; i++) {
            if (vec[i] > max) {
                max = vec[i];
            }
        }
        return max;
    }

時間複雜度爲O( n^2 ),空間複雜度爲O(n)。

method-2

除了動態規劃以外,還存在更簡單的做法。假設存在[a, b], [c, d],其中b < d,此時,[a, b] 形成的最大長度一定大於等於[c, d]所能形成的最大長度,理由就是如果最大長度chain以[c, d]開始,那必然可以以[a, b]開始。於是,只需要對數組進行恰當的排序,便可以簡單地一次循環獲得最大長度。

    int findLongestChain(vector<vector<int>>& pairs) {
        sort(pairs.begin(), pairs.end(), [](std::vector<int> v_1, vector<int> v_2) {
            return v_1[1] < v_2[1] || v_1[1] == v_2[1] && v_1[0] < v_2[0];
        });
        int cnt = 0;
        vector<int>& pair = pairs[0];
        for (int i = 0; i < pairs.size(); i++) {
            if (i == 0 || pairs[i][0] > pair[1]) {
                pair = pairs[i];
                cnt++;
            }
        }
        return cnt;
    }

時間複雜度爲O(nlogn),快排所需要的最短時間;空間複雜度爲O(1)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章