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

 

转载于:https://www.cnblogs.com/forcheryl/p/4569080.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值