当前位置: 首页 > news >正文

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];
}

相关文章:

  • Java接口深度解析
  • B3647 【模板】Floyd
  • 深入 Redis 持久化:从原理到企业级应用的全景图
  • 音视频 五 看书的笔记 MediaCodec
  • 计算机网络 3-3 数据链路层(介质访问控制)
  • 第六章 进阶03 外包测试亮相
  • 第四十六篇 人力资源管理数据仓库架构设计与高阶实践
  • Nginx 命令大全:Linux 与 Windows 系统的全面解析
  • 《计算机名人堂》专栏介绍:先驱之路
  • c# BitMap的data是rgb格式还是bgr格式
  • 【Origin绘图系列第7棒】3D瀑布图
  • python入门:简单介绍和python和pycharm软件安装/学习网址/pycharm设置(改成中文界面,主题,新建文件)
  • Python的那些事第四十九篇:基于Python的智能客服系统设计与实现
  • 蓝桥杯 14g
  • Activiti(五)- 工作流引擎中流程定义删除机制
  • K8s常用基础管理命令(一)
  • PPT模板之--个人简历
  • 安全序列(DP)
  • IO流——字符输入输出流:FileReader FileWriter
  • 【服务器端表单字符验证】
  • wordpress模板学校/什么是白帽seo
  • 建设网站的公司排名/搜索引擎优化seo应用
  • 网站及微站建设合同验收/谷歌seo顾问
  • 做rom的网站/千锋教育和达内哪个好
  • php给一个网站做后台/品牌推广方案范文
  • 南山住房和建设局网站/凡科网站建站教程