leetcode-动态规划25
leetcode-53-最大子数组和
int maxSubArray(int* nums, int numsSize) {int dp[numsSize];dp[0] = nums[0];int ans = nums[0];for(int i = 1 ; i < numsSize ; i++){dp[i] = fmax(dp[i-1]+nums[i],nums[i]);ans = fmax(ans,dp[i]);}return ans;
}
leetcode-392-判断子序列
1.动态规划
本题是编辑距离的入门题目,因为从题意中我们也可以发现,只需要计算删除的情况,不用考虑增加和替换的情况。
dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。(以i为结尾的字符串也可以,与 718.最长重复子数组类似)
(1) if(s[i-1] == t[j-1]) t中找到了一个字符串s中也出现了
那么dp[i][j] = dp[i - 1][j - 1] + 1; 因为找到了一个相同的字符,相同子序列长度自然要在dp[i-1][j-1]的基础上加1
(2) if(s[i-1] != t[j-1]) 相当于t要删除元素,继续匹配
此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i][j] = dp[i][j - 1];
如果要是定义的dp[i][j]是以下标i为结尾的字符串s和以下标j为结尾的字符串t,初始化就比较麻烦了。
(本题和 1143最长公共子序列有相似之处)
bool isSubsequence(char* s, char* t) {int lenS = strlen(s);int lenT = strlen(t);int dp[lenS+1][lenT+1];for(int i = 0 ; i <= lenS ; i++){memset(dp[i],0,sizeof(int)*(lenT+1));}for(int i = 1 ; i <= lenS ; i++){for(int j = 1 ; j <= lenT ; j++){if(s[i-1] == t[j-1]){dp[i][j] = dp[i-1][j-1]+1;}else{dp[i][j] = dp[i][j-1];}}}return dp[lenS][lenT] == lenS;
}
2.双指针
bool isSubsequence(char* s, char* t) {int lenS = strlen(s);int lenT = strlen(t);if(lenS == 0 && lenT == 0)return true;int j= 0;for(int i = 0 ; i < lenT ; i++){if(j < lenS && t[i] == s[j]){j++;}if(j == lenS)return true;}return false;
}
注:如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10 亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
leetcode-115-不同的子序列
统计并返回在
s
的 子序列 中t
出现的个数两种情况:
(1)s[i-1]和t[j-1]相同:dp[i][j]由两部分组成,一部分是由s[i-1]来匹配,个数为dp[i-1][j-1],一部分是由s[i-1]不来匹配,个数为dp[i-1][j]
(2)s[i-1]和t[j-1]不相同:dp[i][j]只能不由s[i-1]来匹配,个数为dp[i-1][j]
因为求的是 s 中有多少个 t,而不是求t中有多少个s,所以只考虑 s中删除元素的情况,即 不用s[i - 1]来匹配 的情况。
初始化:
dp[i][0] 表示:以i-1为结尾的s可以随便删除元素,出现空字符串的个数。那么dp[i][0]一定都是1,因为也就是把以i-1为结尾的s,删除所有元素,出现空字符串的个数就是1。
再来看dp[0][j],dp[0][j]:空字符串s可以随便删除元素,出现以j-1为结尾的字符串t的个数。
那么dp[0][j]一定都是0,s如论如何也变成不了t
int numDistinct(char* s, char* t) {int lenS = strlen(s);int lenT = strlen(t);unsigned long long dp[lenS+1][lenT+1];for(int i = 0 ; i <= lenS ; i++){memset(dp[i],0,sizeof(unsigned long long)*(lenT+1));}for(int i = 0 ; i <= lenS ; i++){dp[i][0] = 1;}for(int i = 1 ; i <= lenS ; i++){for(int j = 1 ; j <= lenT ; j++){if(s[i-1] == t[j-1]){dp[i][j] = dp[i-1][j-1] + dp[i-1][j];}else{dp[i][j] = dp[i-1][j];}}}return dp[lenS][lenT];
}
leetcode-583-两个字符串的删除操作
dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。
(1) 当word1[i - 1] 与 word2[j - 1]相同的时候 dp[i][j] = dp[i-1][j-1]
(2) 当word1[i - 1] 与 word2[j - 1]不相同的时候
情况一:删word1[i - 1],最少操作次数为dp[i - 1][j] + 1
情况二:删word2[j - 1],最少操作次数为dp[i][j - 1] + 1
情况三:同时删word1[i - 1]和word2[j - 1],操作的最少次数为dp[i - 1][j - 1] + 2
那最后当然是取最小值,所以当word1[i - 1] 与 word2[j - 1]不相同的时候,递推公式:dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
因为 dp[i][j - 1] + 1 = dp[i - 1][j - 1] + 2,所以递推公式可简化为:dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);
从字面上理解 就是 当 同时删word1[i - 1]和word2[j - 1],dp[i][j-1] 本来就不考虑 word2[j - 1]了,那么我在删 word1[i - 1],是不是就达到两个元素都删除的效果,即 dp[i][j-1] + 1。
int minDistance(char* word1, char* word2) {int len1 = strlen(word1);int len2 = strlen(word2);int dp[len1+1][len2+1];for(int i = 0 ; i <= len1 ; i++){memset(dp[i],0,sizeof(int)*(len2+1));}for(int i = 0; i <= len1 ; i++) dp[i][0] = i;for(int j = 0 ; j <= len2 ; j++) dp[0][j] = j;for(int i = 1 ; i <= len1 ; i++){for(int j = 1 ; j <= len2 ; j++){if(word1[i-1] == word2[j-1]){dp[i][j] = dp[i-1][j-1]; }else{dp[i][j] = fmin(dp[i-1][j-1] + 2 , fmin(dp[i-1][j] + 1 , dp[i][j-1] + 1));}}}return dp[len1][len2];
}
leetcode-72-编辑距离
dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]
if (word1[i - 1] == word2[j - 1])不操作 if (word1[i - 1] != word2[j - 1])增删换
1. if(word1[i-1] == word2[j-1]) 那么说明不用任何编辑 dp[i][j] = dp[i-1][j-1]
2. if(word1[i-1] != word2[j-1])
(1) 删除操作和添加操作等价
操作一:word1删除一个元素,那么就是以下标i - 2为结尾的word1 与 j-1为结尾的word2的最近编辑距离 再加上一个操作。dp[i][j] = dp[i-1][j] + 1
操作二:word2删除一个元素,那么就是以下标i - 1为结尾的word1 与 j-2为结尾的word2的最近编辑距离 再加上一个操作。dp[i][j] = dp[i][j-1] + 1
注:word2添加一个元素,相当于word1删除一个元素,例如 word1 = "ad", word2 = "a",
word1
删除元素'd'和word2
添加一个元素'd',变成word1 = "a",
word2 = "ad", 最终的操作数是一样!操作三:替换元素,
word1
替换word1[i - 1]
,使其与word2[j - 1]
相同,此时不用增删加元素。dp[i][j] = dp[i-1][j-1] + 1
int minDistance(char* word1, char* word2) {int len1 = strlen(word1);int len2 = strlen(word2);int dp[len1+1][len2+1];for(int i = 0 ; i <= len1 ; i++){memset(dp[i],0,sizeof(int)*(len2+1));}for(int i = 0; i <= len1 ; i++) dp[i][0] = i;for(int j = 0 ; j <= len2 ; j++) dp[0][j] = j;for(int i = 1 ; i <= len1 ; i++){for(int j = 1 ; j <= len2 ; j++){if(word1[i-1] == word2[j-1]){dp[i][j] = dp[i-1][j-1]; }else{dp[i][j] = fmin(dp[i-1][j-1] + 1 , fmin(dp[i-1][j] + 1 , dp[i][j-1] + 1));}}}return dp[len1][len2];
}
leetcode-647-回文子串
布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。
当s[i]与s[j]不相等,dp[i][j]一定是false。
当s[i]与s[j]相等时,这就复杂一些了,有如下三种情况
情况一:下标i 与 j相同,同一个字符例如a,当然是回文子串
情况二:下标i 与 j相差为1,例如aa,也是回文子串
情况三:下标:i 与 j相差大于1的时候,例如cabac,此时s[i]与s[j]已经相同了,我们看i到j区间是不是回文子串就看aba是不是回文就可以了,那么aba的区间就是 i+1 与 j-1区间,这个区间是不是回文就看dp[i + 1][j - 1]是否为true。
没有列出当s[i]与s[j]不相等的时候,因为在下面dp[i][j]初始化的时候,就初始为false。
注意因为dp[i][j]的定义,所以j一定是大于等于i的,那么在填充dp[i][j]的时候一定是只填充右上半部分。
int countSubstrings(char* s) {int len = strlen(s);bool dp[len][len];for(int i = 0 ; i < len ; i++){memset(dp[i],false,sizeof(bool)*len);}int res = 0;for(int i = len-1; i >= 0 ; i--){for(int j = i ; j < len ; j++){if(s[i] == s[j]){if(j - i <= 1){dp[i][j] = true;res++;}else if(dp[i+1][j-1]){dp[i][j] = true;res++;}}}}return res;
}
leetcode-516-最长回文子序列
dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]。
(1) 如果s[i]与s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;
(2) 如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。
加入s[j]的回文子序列长度为dp[i + 1][j]。
加入s[i]的回文子序列长度为dp[i][j - 1]。
那么dp[i][j]一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
初始化:
当i与j相同,那么dp[i][j]一定是等于1的,即:一个字符的回文子序列长度就是1。
注:从递归公式中,可以看出,dp[i][j] 依赖于 dp[i + 1][j - 1] ,dp[i + 1][j] 和 dp[i][j - 1]
所以遍历i的时候一定要从下到上遍历,这样才能保证下一行的数据是经过计算的
int longestPalindromeSubseq(char* s) {int len = strlen(s);int dp[len][len];for(int i = 0 ; i < len ; i++){memset(dp[i],0,sizeof(int)*len);}for(int i = len-1 ; i >= 0 ; i--){dp[i][i] = 1;for(int j = i+1 ; j < len ; j++){if(s[i] == s[j]){dp[i][j] = dp[i+1][j-1] + 2;}else{dp[i][j] = fmax(dp[i][j],fmax(dp[i+1][j],dp[i][j-1]));}}}return dp[0][len-1];
}