同学们可能对什么使用正序求解,什么时候倒序求解有一点疑问。
我们现在说的是一种bottom-up的方式,每个大问题用子问题去求解,保证每次要用到的子问题都已经先被计算过了,我们也把它叫做递推(recurrence)。
还有一种叫做记忆化搜索(memorization),就是递归得求答案,并把算过的内容存在数组里,因为它是从上往下算的,这是一种top-down的方式。
一般来说动态规划题两种都可以用,但是,有些时候某一种写法能比另一种写法简单很多,所以两种写法都要掌握

107. Word Break

DP需要建立对应表,将它一一填入
unordered_set找是否在 dict中

  1. class Solution {
  2. public:
  3. /*
  4. * @param s: A string
  5. * @param dict: A dictionary of words dict
  6. * @return: A boolean
  7. */
  8. bool wordBreak(string &s, unordered_set<string> &dict) {
  9. // write your code here
  10. int m = s.size();
  11. vector<bool> result(m + 1, false);
  12. result[0] = true;
  13. for (int i = 0; i < s.size(); i ++){
  14. for (int j = i; j < s.size(); j ++){
  15. if (result[i] != true){
  16. continue;
  17. }
  18. // cout << s.substr(i, j - i + 1);
  19. if (dict.count(s.substr(i, j - i + 1))){
  20. result[j + 1] = true;
  21. }
  22. }
  23. }
  24. return result[m];
  25. }
  26. };

109 Triangle

  1. class Solution {
  2. public:
  3. /**
  4. * @param triangle: a list of lists of integers
  5. * @return: An integer, minimum path sum
  6. */
  7. int minimumTotal(vector<vector<int>> &triangle) {
  8. // write your code here
  9. int size = triangle.size();
  10. vector<vector<int>> res(size, vector<int>(size));
  11. res[0][0] = triangle[0][0];
  12. for(int i = 1; i < size; i++){
  13. res[i][0] = res[i - 1][0] + triangle[i][0];
  14. res[i][i] = res[i - 1][i - 1] + triangle[i][i];
  15. }
  16. for(int i = 2; i < size; i++){
  17. for (int j = 1; j < i ; j++){
  18. res[i][j] = min(res[i - 1][j] , res[i - 1][j - 1]) + triangle[i][j];
  19. }
  20. }
  21. int min = INT_MAX;
  22. for (int i = 0; i < size; i++){
  23. if (res[size - 1][i] < min){
  24. min = res[size - 1][i];
  25. }
  26. }
  27. return min;
  28. }
  29. };

683. Word Break III

class Solution {
public:
    /*
     * @param : A string
     * @param : A set of word
     * @return: the number of possible sentences.
     */
    int wordBreak3(string& s, unordered_set<string>& dict) {
        // Write your code here
        vector<int> res(s.size() + 1);
        transform(s.begin(), s.end(), s.begin(), ::tolower);
        unordered_set<string> hs;
        for(string x : dict) {
            transform(x.begin(), x.end(), x.begin(), ::tolower);
            hs.insert(x);
        }
        dict = hs;
        for (int i = 1; i < s.size() + 1; i++){
            if (dict.find(s.substr(0, i)) != dict.end()){
                res[i] = 1;
            }
            for (int j = 0; j < i; j++){
                if(dict.find(s.substr(j, i - j)) != dict.end()){
                    res[i] += res[j];
                }
            }
        }
        return res[s.size()];
    }
};

word break 2

记忆化搜索

class Solution {
public:
    /*
     * @param s: A string
     * @param wordDict: A set of words.
     * @return: All possible sentences.
     */
    vector<string> wordBreak(string &s, unordered_set<string> &wordDict) {
        // write your code here
        vector<string> results;
        string subsets;
        helper(s, 0, wordDict, results, subsets);
        return results;
    }
    void helper(string & s, int index, unordered_set<string> & wordDict, vector<string> & results, string & subsets){
        if (index == s.size()){
            results.push_back(subsets);
            return;
        }
        if (index > s.size()){
            return;
        }
        if (index > 0){
            subsets += " ";
        }
        for (int i = index; i < s.size(); i ++){
            if (wordDict.count(s.substr(index, i - index + 1))){
                int temp = subsets.size();
                subsets += s.substr(index, i - index + 1);
                helper(s, i + 1, wordDict, results, subsets);
                if (index == 0){
                    subsets = subsets.substr(0, 0);
                }else{
                    subsets = subsets.substr(0, temp);
                }

            }
        }
        if (index > 0){
            subsets.pop_back();
        }
    }
};

DP

class Solution {
    public List<String> wordBreak(String s, List<String> wordDict) {
        Map<String,List<String>> record = new HashMap<>();
        return subwordBreak(s,wordDict,record);

    }
    public List<String> subwordBreak(String s, List<String> wordDict, Map <String,List<String>> record){
      if(record.containsKey(s)){
          return record.get(s);
      }
        List<String> answer = new ArrayList<>();
        if(s.length()==0){
            return answer;
        }
        for(int i=1;i<=s.length();i++){
            String p = s.substring(0,i);
            if(wordDict.contains(p)){
                if(s.length()==i){
                    answer.add(p);
                }else{
                    List<String> sub = new ArrayList<>();
                    if(record.containsKey(s.substring(i,s.length()))){
                        sub = record.get(s.substring(i,s.length()));
                    }else{
                        sub =subwordBreak(s.substring(i,s.length()),wordDict,record);
                        record.put(s.substring(i,s.length()),sub);
                    }
                    if(sub.size()>0){
                        for(String m : sub){
                            answer.add(p+" "+m);
                        }
                    }
                }
            }
        }
        return answer;
    }
}

wildcard matching

DP

class Solution {
public:
    /**
     * @param s: A string 
     * @param p: A string includes "?" and "*"
     * @return: is Match?
     */
    bool isMatch(string &s, string &p) {
        // write your code here
        int m = s.size();
        int n = p.size();
        vector<vector<bool>> DP (m + 1, vector<bool> (n + 1, false));
        DP[0][0] = true;
        for(int i = 1; i <= m; i ++){
            DP[i][0] = false;
        }
        for (int i = 1; i <= n; i ++){
            DP[0][i] = DP[0][i - 1] && (p[i - 1] == '*');
        }
        for (int j = 1; j <= m; j ++){
            for (int i = 1; i <= n; i ++){
                if (p[i - 1] == '*'){
                    DP[j][i] = DP[j][i - 1]|| DP[j - 1][i];
                }else if(p[i - 1] == '?'){
                    DP[j][i] = DP[j - 1][i - 1];
                }else{
                    DP[j][i] = DP[j - 1][i - 1] && (s[j - 1] == p[i - 1]);
                }
                cout << j << ' ' << i << ' ' << DP[j][i] << endl;
            }
        }
        return DP[m][n];

    }
};

记忆化搜索

class Solution {
public:
    /**
     * @param s: A string
     * @param p: A string includes "?" and "*"
     * @return: is Match?
     */
    bool isMatch(string &s, string &p) {
        // write your code here
        unordered_map<pair<int, int>, bool, pair_hash> resVec;
        return (matchHelper(s, 0, p, 0, resVec));
    }
    struct pair_hash
    {
        template<class T1, class T2>
        std::size_t operator() (const std::pair<T1, T2>& p) const
        {
            auto h1 = std::hash<T1>{}(p.first);
            auto h2 = std::hash<T2>{}(p.second);
            return h1 ^ h2;
        }
    };


    bool matchHelper(string &s, int s_len, string &p, int p_len, unordered_map<pair<int, int>, bool, pair_hash> &resVec) {
        pair<int, int> combination = make_pair(s_len, p_len);
        if (resVec.find(combination) != resVec.end()) {
            return resVec[combination];
        }
        if (s_len == s.size() && p_len == p.size()) {
            resVec[combination] = true;
            return true;
        }
        if (s_len > s.size() || p_len >= p.size()) {
            resVec[combination] = false;
            return false;
        }
        bool matched;
        if (p[p_len] != '*') {
            if (matchChar(s[s_len], p[p_len])) {
                matched = matchHelper(s, s_len + 1, p, p_len + 1, resVec);
            }
        }
        else{
            matched = (matchHelper(s, s_len, p, p_len + 1, resVec) || matchHelper(s, s_len + 1, p, p_len, resVec));
        }
        resVec[combination] = matched;
        return matched;
    }

    bool matchChar(char &a, char &b) {
        if (a == b or b == '?') {
            return true;
        }
        return false;
    }
};

154 Regular Expression Matching

DFS

class Solution {
public:
    bool isMatch(string s, string p) {
        if (p.empty()) return s.empty();
        if (p.size() == 1) {
            return (s.size() == 1 && (s[0] == p[0] || p[0] == '.'));
        }
        if (p[1] != '*') {
            if (s.empty()) return false;
            return (s[0] == p[0] || p[0] == '.') && isMatch(s.substr(1), p.substr(1));
        }
        while (!s.empty() && (s[0] == p[0] || p[0] == '.')) {
            if (isMatch(s, p.substr(2))) return true;
            s = s.substr(1);
        }
        return isMatch(s, p.substr(2));
    }
};

DP

class Solution {
public:
    /**
     * @param s: A string 
     * @param p: A string includes "." and "*"
     * @return: A boolean
     */
    bool isMatch(string &s, string &p) {
        // write your code here
        int m = p.size();
        int n = s.size();
        vector<vector<bool>> DP (m + 1, vector<bool> (n + 1, false));
        DP[0][0] = true;
        for (int i = 1; i <= n; i ++){
            DP[0][i] = false;
        }
        for (int i = 1; i <= m; i ++){
            if (p[i - 1] == '*'){
                continue;
            }else if (i < m && p[i] == '*'){
                DP[i][0] = DP[i - 1][0];
                DP[i + 1][0] = DP[i - 1][0];
            }else{
                DP[i][0] = false;
            }
           // cout << i << '0' << DP[i][0];
        }
        for (int i = 1; i <= m; i ++){
            for (int j = 1; j <= n; j ++){
                if (p[i - 1] == '*'){
                    continue;
                }else if (i < m && p[i] == '*' && p[i - 1] == '.'){
                    DP[i][j] = DP[i][j - 1]|| DP[i - 1][j];
                    DP[i + 1][j] = DP[i][j - 1]|| DP[i - 1][j];
                }else if (i < m && p[i] == '*' && isalpha(p[i - 1])){
                    if (p[i - 1] == s[j - 1]){
                        DP[i][j] = DP[i][j - 1] || DP[i - 1][j];
                        DP[i + 1][j] = DP[i][j - 1]|| DP[i - 1][j];
                    }else{
                        DP[i][j] = DP[i - 1][j];
                        DP[i + 1][j] = DP[i - 1][j];
                    }
                }else if (p[i - 1] == '.'){
                    DP[i][j] = DP[i - 1][j - 1];
                }else if (isalpha(p[i - 1])){
                    if (p[i - 1] == s[j - 1]){
                        DP[i][j] = DP[i - 1][j - 1];
                    }else{
                        DP[i][j] = false;
                    }
                } 
               // cout << i << j << DP[i][j];
            }
        }
        return DP[m][n];
    }
};

思路基本类似,使用记忆化搜索, 但是要想清楚else里面的判断

class Solution {
public:
    /**
     * @param s: A string 
     * @param p: A string includes "." and "*"
     * @return: A boolean
     */
    bool isMatch(string &s, string &p) {
        // write your code here
        unordered_map <int, bool> memo;
        return matchHelper(s, 0, p, 0, memo);
    }
    bool matchHelper(string &s, int s_temp, string &p, int p_temp, unordered_map <int, bool> &memo){
        if (memo.find(s_temp + s.size() * p_temp) != memo.end()){
            return memo[s_temp + s.size() * p_temp];
        }
        if (s_temp == s.size() && p_temp == p.size()){
            return true;
        }
        bool matched;
        if (s_temp == s.size() && (p_temp + 1 < p.size() && p[p_temp + 1] == '*')){
            matched = matchHelper(s, s_temp, p, p_temp + 2, memo);
            memo[s_temp + s.size() * p_temp] = matched;
            return matched;
        }
        if (s_temp > s.size() || p_temp >= p.size()){
            return false;
        }

        if (p_temp == (p.size() - 1) || (p_temp + 1 < p.size() && p[p_temp + 1] != '*')){
            if (charMatch(s[s_temp], p[p_temp])){
                matched = matchHelper(s, s_temp + 1, p, p_temp + 1, memo);
            }
        }
        else{
            if(!charMatch(s[s_temp], p[p_temp])){
                matched = matchHelper(s, s_temp, p, p_temp + 2, memo);
            }
            else{
                matched = matchHelper(s, s_temp + 1, p, p_temp, memo) || matchHelper(s, s_temp, p, p_temp + 2, memo);
            }
        }
        memo[s_temp + s.size() * p_temp] = matched;
        return matched;
    }
    bool charMatch(char a, char b){
        if (a == b || b == '.'){
            return true;
        }
        return false;
    }
};

110. Minimum Path Sum

class Solution {
public:
    /**
     * @param grid: a list of lists of integers
     * @return: An integer, minimizes the sum of all numbers along its path
     */
    int minPathSum(vector<vector<int>> &grid) {
        // write your code here
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> sum (m, vector<int>(n, 0));
        sum[0][0] = grid[0][0];
        for (int i = 0; i < m; i ++){
            for (int j = 0; j < n; j ++){
                if (i == 0 && j > 0){
                    sum[i][j] = sum[i][j - 1] + grid[i][j];
                }
                if (i > 0 && j == 0){
                    sum[i][j] = sum[i - 1][j] + grid[i][j];
                }
                if (i > 0 && j > 0){
                    sum[i][j] = min(sum[i - 1][j], sum[i][j - 1]) + grid[i][j]; 
                }
            }
        }
        return sum[m - 1][n - 1];
    }
};

603. Largest Divisible Subset

better version by laoshi
记录点的种类数和father

class Solution:
    # @param {int[]} nums a set of distinct positive integers
    # @return {int[]} the largest subset 
    def largestDivisibleSubset(self, nums):
        # Write your code here
        n = len(nums)
        dp = [1] * n
        father = [-1] * n

        nums.sort()
        m, index = 0, -1
        for i in xrange(n):
            for j in xrange(i):
                if nums[i] % nums[j] == 0:
                    if 1 + dp[j] > dp[i]:
                        dp[i] = dp[j] + 1
                        father[i] = j

            if dp[i] >= m:
                m = dp[i]
                index = i

        result = []
        for i in xrange(m):
            result.append(nums[index])
            index = father[index]

        return result

my submission

class Solution {
public:
    /*
     * @param nums: a set of distinct positive integers
     * @return: the largest subset 
     */
    vector<int> largestDivisibleSubset(vector<int> &nums) {
        // write your code here
        sort(nums.begin(), nums.end());
        vector<vector<int>> res(nums.size());
        for (int i = 0; i < nums.size(); i ++){
            int iTemp = 0;
            for (int j = 0; j < i; j ++){
                if ((nums[i] % nums[j]) == 0 && res[j].size() > iTemp){
                    iTemp = res[j].size();
                    res[i] = res[j];
                    res[i].push_back(nums[i]);
                }
            }
            if (iTemp == 0){
                res[i].push_back(nums[i]);
            }
        }
        int maxNum = 0;
        int maxPos = 0;
        for (int i = 0; i < nums.size(); i ++){
            if(res[i].size() > maxNum){
                maxNum = res[i].size();
                maxPos = i;
            }
        }
        return res[maxPos];
    }
};

knight shortest path

遍历数组:for(auto i : nums)

class Solution {
public:
    /**
    * @param grid: a chessboard included 0 and 1
    * @return: the shortest path
    */
    int shortestPath2(vector<vector<bool>> &grid) {
        // write your code here
        int m = grid.size();
        int n = grid[0].size();
        vector<vector<int>> d = { { -2, 1 },{ 2, 1 },{ -1, 2 },{ 1, 2 } };
        vector<vector<int>> path(m, vector<int>(n, m*n));
        path[0][0] = 0;
        for (int j = 0; j < n - 1; j++) {
            for (int i = 0; i < m; i++) {
                if (grid[i][j] != 1) {
                    for (int k = 0; k < 4; k++) {
                        if (i + d[k][0] >= m || i + d[k][0] < 0 || j + d[k][1] >= n) {
                            continue;
                        }
                        if (grid[i + d[k][0]][j + d[k][1]] != 1 && path[i + d[k][0]][j + d[k][1]] > path[i][j] + 1) {
                            path[i + d[k][0]][j + d[k][1]] = path[i][j] + 1;
                        }
                    }
                }
            }
        }
        if (path[m - 1][n - 1] == m*n){
            return -1;
        }
        return path[m - 1][n - 1];
    }
};

76. Longest Increasing Subsequence

image.png

class Solution {
public:
    /**
     * @param nums: An integer array
     * @return: The length of LIS (longest increasing subsequence)
     */
    int longestIncreasingSubsequence(vector<int> &nums) {
        // write your code here
        // if (nums.size() == 0){
        //     return 0;
        // }
        int m = nums.size();
        vector<int> seq(m, INT_MAX);
        for (int i = 0; i < m; i ++){
            int temp = lower_bound(seq.begin(), seq.end(), nums[i]) - seq.begin();
            cout << temp << endl;
            seq[temp] = nums[i];
        }
        int temp = 0;
        while(temp < m && seq[temp] != INT_MAX){
            temp ++;
        }
        return temp;
    }
};
class Solution {
public:
    /**
     * @param nums: An integer array
     * @return: The length of LIS (longest increasing subsequence)
     */
    int longestIncreasingSubsequence(vector<int> &nums) {
        // write your code here
        if (nums.size() == 0){
            return 0;
        }
        //存每个数为结尾的最长子序列
        //vector <int> dp (nums.size());
        //存储dp值为i的最小的数字
        vector <int> res (nums.size() + 1);
        for (int i = 1; i < nums.size() + 1; i ++){
            res[i] = INT_MAX;
        }
        res[1] = 0;
        int temp = 1;
        for (int i = 0; i < nums.size(); i ++){
            int index = binarySearch(nums, res, temp, i);
            if (index == temp){
                temp ++;
            }
            res[index] = nums[i];
            //cout << index << ' ' << res[index]<< endl;
        }
        temp = 0;
        for (int i = 1; i < nums.size() + 1; i ++){
            if (res[i] == INT_MAX){
                break;
            }
            temp ++;
        }
        return temp;

    }
    int binarySearch(vector <int> & nums, vector <int> &res, int temp, int i){
        int start = 0, end = temp;
        while (start + 1 < end){
            int mid = start + (end - start) / 2;
            if (res[mid] < nums[i]){
                start = mid;
            }else{
                end = mid;
            }
        } 
        return end;
    }
};

602 Russian Doll

O(n2)方法

class Solution {
public:
    /*
     * @param envelopes: a number of envelopes with widths and heights
     * @return: the maximum number of envelopes
     */
    int maxEnvelopes(vector<pair<int, int>>& envelopes) {
        // write your code here
        sort(envelopes.begin(), envelopes.end());
        //vector <pair<int, int>> previous(envelopes.size());
        vector <int> res(envelopes.size(), 1);
        for (int i = 1; i < envelopes.size(); i ++){
            int temp = 1;
            for(int j = 0; j < i; j ++){
                if (envelopes[i].first > envelopes[j].first && envelopes[i].second > envelopes[j].second && res[j] + 1 > temp){
                    temp = res[j] + 1;
                    //previous[i] = envelopes[j];
                }
            }
            res[i] = temp;
        }
        auto resPos = max_element(res.begin(), res.end());
        return res[resPos - res.begin()];
    }
};

O(nlogn)
[[4,5],[4,6],[6,7],[2,3],[1,1]]
[1,1] [2, 8][4,6][4,5] [6,7]
dp[0]=0;
height[0]=1;
dp[1]=1;
height[1]=8; 3
dp[2]=1; 2
height[1]=6; 6
dp[3]=1; 3
height[1]=5; 5
dp[4]=2; 5
height[2]=7 7

bool cmp(const pair<int,int>&x, const pair<int, int>&y) {
  return x.first != y.first ? x.first < y.first : x.second > y.second;
}

class Solution {
public:
    /**
     * @param envelopes a number of envelopes with widths and heights
     * @return the maximum number of envelopes
     */
    int maxEnvelopes(vector<pair<int, int>>& envelopes) {
        // Write your code here
        int n = envelopes.size();
        if (n == 0) {
            return 0;
        }

        sort(envelopes.begin(), envelopes.end(), cmp);
        vector<int> dp(n), height(n+1, INT_MAX);
        for (int i = 0; i < n; i++) {
            int k = lower_bound(height.begin(), height.end(), envelopes[i].second) - height.begin() ;
            dp[i] = k;
            height[k] = envelopes[i].second;
        }

        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans = max(ans, dp[i]);
        }
        return ans + 1;
    }
};

309. Best Time to Buy and Sell Stock with Cooldown

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int m = prices.size();
        vector<int> buy(m, 0);
        vector<int> sell(m, 0);
        if (m == 0 || m == 1){
            return 0;
        }
        buy[0] = - prices[0];
        sell[0] = 0;
        buy[1] = max(- prices[1], buy[0]);
        sell[1] = max(buy[0] + prices[1], sell[0]);
        for(int i = 2; i < m; i ++){
            buy[i] = max(sell[i - 2] - prices[i], buy[i - 1]);
            sell[i] = max(buy[i - 1] + prices[i], sell[i - 1]);
        }
        return sell[m - 1];
    }
};