• leetcode Ch2-Dynamic Programming II


    一、 Longest Valid Parentheses

    方法一、一维DP

     1 class Solution
     2 {
     3 public:
     4     int longestValidParentheses(string s)
     5     {
     6         vector<int> dp(s.size(),0);
     7         int res=0;
     8         for(int i=s.size()-2;i>=0;i--)
     9         {
    10             if(s[i]==')')
    11                 dp[i]=0;
    12             else
    13             {
    14                 int j=i+dp[i+1]+1;
    15                 if(j<s.size()&&s[j]==')')
    16                 {
    17                     dp[i]+=dp[i+1]+2;
    18                     if(j+1<s.size())
    19                         dp[i]+=dp[j+1];
    20                 }
    21             }
    22             res=max(res,dp[i]);
    23         }
    24         return res;
    25     }
    26 };
    View Code

    像这种连续的子串,类似于最大连续子串和,在dp时往往是以某位置为结尾/起点。

    dp[i]表示以s[i]为起点的最长连续括号数。下标j表示跨越了以s[i+1]为起点的一连串括号后到达的位置。如果该位置s[j]为'(' ,那说明以该'('为起点的有效括号数必然是0(否则就应该被包含进以s[i+1]为起点的有效括号串里了);而如果该位置s[j]为')',那正好和s[i]的'('匹配起来,因此s[i]可基于s[i+1]的基础上再加2,而且,除此之外还要再加上s[j+1] 。

    ref1: Lexi's

    #2: 忘的太快。很多细节: 判断 j < n 和 j + 1 < n。

    方法二、栈

    二、 Regular Expression Matching

    DP解法

    ref1   ref2

     三、 Interleaving String

     1 class Solution
     2 {
     3 public:
     4     bool isInterleave(string s1,string s2,string s3)
     5     {    
     6         int m=s1.size(),n=s2.size();
     7         if(m+n!=s3.size()) return false;    
     8         vector<vector<int>> f(m+1,vector<int>(n+1,0));
     9         f[0][0]=true;
    10         for(int i=1;i<=m;i++)
    11             f[i][0]=f[i-1][0]&&(s1[i-1]==s3[i-1]);
    12         for(int i=1;i<=n;i++)
    13             f[0][i]=f[0][i-1]&&(s2[i-1]==s3[i-1]);
    14         for(int i=1;i<=m;i++)
    15             for(int j=1;j<=n;j++)
    16                 f[i][j]=(s1[i-1]==s3[i+j-1] && f[i-1][j])||(s2[j-1]==s3[i+j-1] && f[i][j-1]);
    17         return f[m][n];
    18     }
    19 };
    View Code

    f[i][j]表示s1从下标0开始长度为i的子串,与s2从下标0开始长度为j的子串,它们与s3从0开始长度为i+j的子串是否是interleave string。

    注意这里的i,j都是长度。所以转换为下标时别忘了减1.

    另外,在line8初始化时如果是以0为初始值,那就别忘了专门把f[0][0]赋值为1。

    细节很多,易出错。

    #2: good.

    四、 Maximal Square

     1 class Solution
     2 {
     3 public:
     4     int maximalSquare(vector<vector<char>> &matrix)
     5     {
     6         if(matrix.empty()) return 0;
     7         int m=matrix.size(),n=matrix[0].size();
     8         vector<vector<int>> f(m,vector<int>(n,0));
     9         int res=0;
    10         for(int i=0;i<m;i++)
    11         {
    12             f[i][0]=matrix[i][0]-'0';
    13             res=max(res,f[i][0]);
    14         }
    15         for(int i=0;i<n;i++)
    16         {    
    17             f[0][i]=matrix[0][i]-'0';
    18             res=max(res,f[0][i]);
    19         }
    20         for(int i=1;i<m;i++)
    21         {
    22             for(int j=1;j<n;j++)
    23             {
    24                 if(matrix[i][j]=='1')
    25                 {
    26                     f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1;
    27                     res=max(res,f[i][j]);
    28                 }
    29                 else f[i][j]=0;
    30             }
    31         }
    32         return res*res;
    33     }
    34 };
    View Code

    dp[i][j]表示以(i,j)为右下角的最大正方形的边长。那么,可得递推公式:dp[i][j]= min( dp[i-1][j] , dp[i][j-1] dp[i-1][j-1] ) + 1;

    该递推式可以先直观感受一下:

    若该点周围3个点最小的dp值为0(即至少有一个点是‘0’),那最大正方形边长必然是1(即该点本身);

    若该点周围3个点最小的dp值为1,那么至少这3个点都是‘1’,所以已经可以构成一个边长为2的正方形;

    ...

    ref

    #2: 一堆细节易出错。题目要求的是面积,不是边长,所以最后返回的是res * res; 第一行和第一列要特殊处理,同时把它们的值都要跟res比一下并更新res。

    五、 Palindrome Partitioning II

     1 class Solution
     2 {
     3 public:
     4     int minCut(string s)
     5     {
     6         int n=s.size();
     7         vector<int> f(n+1,0);
     8         vector<vector<int>> p(n,vector<int>(n,0));
     9         for(int i=0;i<=n;i++)
    10             f[i]=n-1-i;
    11         for(int i=n-1;i>=0;i--)
    12         {
    13             for(int j=i;j<n;j++)
    14             {
    15                 if(s[i]==s[j]&&(j-i<2||p[i+1][j-1]))
    16                 {
    17                     p[i][j]=1;
    18                     f[i]=min(f[i],f[j+1]+1);
    19                 }
    20             }
    21         }
    22         return f[0];
    23     }
    24 };
    View Code

    设len=s.size(),  f[i]表示从s[i]开始一直到s[len-1]这段子串的minCut数(显然,最大值即为这段子串包含的字符数减1)。

    这里之所以有除了f[0]~f[len-1]之外还有个f[len]= -1,是因为后面会用到。

    p[i][j]表示s[i...j]这段子串是否是回文串。判断依据就是 if ( s[i]==s[j]  &&  (j-i<2 || p[i+1][j-1]) ) ,这个如果成立则说明s[i...j]是个回文串,那么就可以尝试更新一下  f[i]=min(f[i] , f[j+1]+1) ,即如果在j和j+1之间cut一下,那么s[i...len-1]即分成了s[i...j]和s[j+1...len-1],此时的cut数等于f[j+1]+1.

    ref: soul, spring 

    #2: 太容易忘。

    六、 Maximum Product Subarray

     O(n) space: 【非最优】

     1 class Solution
     2 {
     3 public:
     4     int maxProduct(vector<int> &nums)
     5     {
     6         int n=nums.size();    
     7         vector<int> dp_max(n,0);
     8         vector<int> dp_min(n,0);
     9         int res=dp_max[0]=dp_min[0]=nums[0];
    10         for(int i=1;i<n;i++)
    11         {
    12             dp_max[i]=max(nums[i],max(dp_max[i-1]*nums[i],dp_min[i-1]*nums[i]));
    13             dp_min[i]=min(nums[i],min(dp_max[i-1]*nums[i],dp_min[i-1]*nums[i]));    
    14             res=max(res,dp_max[i]);
    15         }
    16         return res;
    17     }
    18 };
    View Code

    O(1) space 【最优】

     1 class Solution
     2 {
     3 public:
     4     int maxProduct(vector<int> &nums)
     5     {
     6         int n=nums.size();    
     7         int maxsofar,minsofar,maxpre,minpre;
     8         int res=maxsofar=minsofar=maxpre=minpre=nums[0];
     9         for(int i=1;i<n;i++)
    10         {
    11             maxsofar=max(nums[i],max(maxpre*nums[i],minpre*nums[i]));
    12             minsofar=min(nums[i],min(maxpre*nums[i],minpre*nums[i]));    
    13             res=max(res,maxsofar);
    14             maxpre=maxsofar;
    15             minpre=minsofar;
    16         }
    17         return res;
    18     }
    19 };
    View Code

    #2: 细节容易出错。

    七、 Decode Ways

     1 class Solution
     2 {
     3 public:
     4     int numDecodings(string s)    
     5     {
     6         int n=s.size();
     7         if(n==0) return 0;
     8         vector<int> dp(n+1,0);
     9         dp[0]=1;
    10         if(s[0]!='0') dp[1]=1;
    11         for(int i=2;i<=n;i++)
    12         {
    13             if(s[i-1]!='0')
    14                 dp[i]+=dp[i-1];
    15             if(s[i-2]=='1'||(s[i-2]=='2' && s[i-1]<='6'))
    16                 dp[i]+=dp[i-2];
    17         }
    18         return dp[n];
    19     }
    20 };
    View Code

    dp[i]里的i表示长度,即从下标0开始的长度为i的子串的decode ways数。

    要特别注意,dp[0]值为1。这是为了递推后面的值的需要。

    与climb stairs是类似的。  可以用滚动数组优化空间复杂度。

    ref

    Backpack

    Backpack II

    k Sum

    k Sum II

    Minimum Adjustment Cost

  • 相关阅读:
    TCP性能调优
    Qt 实现应用程序单实例运行
    table多选
    多选删除最佳处理
    获取路由或路径
    当前页面打开新页面
    vue版本更新index.html缓存
    vue项目js和css文件名避免浏览器缓存再vue.config.js中配置
    vue动态表格
    IE网页被缓存,get接口缓存
  • 原文地址:https://www.cnblogs.com/forcheryl/p/4569080.html
Copyright © 2020-2023  润新知