DP:回文串模型

news2024/12/25 9:00:20

一、回文子串

. - 力扣(LeetCode)

 该题有3种解法

(1)中心扩展算法(在字符串章节有介绍)时间复杂度O(N^2),空间复杂度O(1)

(2)马丁车算法(专门用来解决回文串问题,但是适用返回太窄)时间复杂度O(N),空间复杂度O(N)

(3)动态规划(可以将所有回文信息都保存在dp表中)时间复杂度O(N^2),空间复杂度O(N^2)

这边重点介绍动态规划的做法。

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示s字符串[i,j]的子串是否是回文串(i<=j)只需处理右上区即可

 2、状态转移方程

dp[i][j]:  

(1)s[i]!=s[j]——>false

(2)s[i]==s[j]——>

      i==j  true 

      i+1==j   true

      dp[i+1][j-1]

3、初始化

无需初始化

4、填表顺序

dp[i][j]会用到dp[i+1][j-1],所以必须要从下往上填 , 左右顺序不重要

5、返回值

dp表中true的个数

class Solution {
public:
    int countSubstrings(string s) {
      //动态规划的做法
      int ret=0;
      //s[i]==s[j]  1、i==j  2、i+1==j  3、dp[i+1][j-1]?
      int n=s.size();
      vector<vector<bool>> dp(n,vector<bool>(n));
      //只要右上半区 
      for(int i=n-1;i>=0;--i)  //要从下往上  左右无所谓,因为用不到
        for(int j=i;j<n;++j) //只要右上半区
            if(s[i]==s[j]) ret+=dp[i][j]=i+1<j?dp[i+1][j-1]:true;
      return ret;
    }
};

 二、最长回文子串

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示s字符串[i,j]的子串是否是回文串(i<=j)只需处理右上区即可

 2、状态转移方程

dp[i][j]:  

(1)s[i]!=s[j]——>false

(2)s[i]==s[j]——>

      i==j  true 

      i+1==j   true

      dp[i+1][j-1]

3、初始化

无需初始化

4、填表顺序

dp[i][j]会用到dp[i+1][j-1],所以必须要从下往上填 , 左右顺序不重要

5、返回值

dp表中为true以及长度最大的子串的起始位置和长度

class Solution {
public:
    string longestPalindrome(string s) {
    //动态规划的思想
    int begin=0,len=1;//帮助我们返回结果
    int n=s.size();
    vector<vector<bool>> dp(n,vector<bool>(n));
    for(int i=n-1;i>=0;--i)
      for(int j=i;j<n;++j) //右上角部分
        {
            if(s[i]==s[j]) 
            {
                dp[i][j]=i+1<j?dp[i+1][j-1]:true;
                if(dp[i][j]&&len<j-i+1) 
                {
                    begin=i;
                    len=j-i+1;
                }
            }  
        }
    return s.substr(begin,len);
    }
};

三、分割回文子串I

. - 力扣(LeetCode)

解法1:动归预处理+回溯

class Solution {
public:
   //动归预处理+回溯
    vector<vector<bool>> dp;//dp预处理
    vector<vector<string>> ret;//记录返回的结果
    vector<string> path;//记录路径的结果
    int n;
    vector<vector<string>> partition(string s) {
       //dp预处理
       n=s.size();
       dp.resize(n,vector<bool>(n));
       for(int i=n-1;i>=0;--i)
          for(int j=i;j<n;++j)
            if(s[i]==s[j])  dp[i][j]=i+1<j?dp[i+1][j-1]:true;
        //将dp数组交给dfs去处理
        dfs(s,0);
        return ret;
    }
    void dfs(string&s,int i)
    {
        if(i==n) 
        {
            ret.push_back(path);
            return;
        }
        for(int j=i;j<n;++j)
            if(dp[i][j])
            {
                path.emplace_back(s.substr(i,j-i+1));
                dfs(s,j+1);
                path.pop_back();
            }
    }
};

 解法2:回溯+记忆化搜索

class Solution {
public:
//回溯+记忆化搜索
    vector<vector<int>> f;//记忆化数组  0表示未搜索,1表示回文,-1表示不回文
    vector<vector<string>> ret;//记录返回的结果
    vector<string> path;//记录路径的结果
    int n;
    vector<vector<string>> partition(string s) {
      n=s.size();
      f.resize(n,vector<int>(n));
      //交给dfs帮助我们解决
      dfs(s,0);
      return ret;
    }
    void dfs(const string&s,int i)
    {
        if(i==n) 
        {
            ret.emplace_back(path);
            return;
        }
        for(int j=i;j<n;++j)
          if(ispal(s,i,j))
        {
            path.emplace_back(s.substr(i,j-i+1));
            dfs(s,j+1);
            path.pop_back();
        }
    }
    bool ispal(const string&s,int i,int j) //判断i->j是否回文
    {
       //先看看备忘录
       if(f[i][j]) return f[i][j];
       if(s[i]!=s[j]) return f[i][j]=false;
       else return f[i][j]=i+1<j?ispal(s,i+1,j-1):true;
    }
};

四、分割回文子串II

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i]表示s字符串[0,i]区间上的最长子串的最小分割次数

 2、状态转移方程

dp[i]:  

(1)0-i回文——>0

(2)0-i不是回文——>j-i是否回文——>min(dp[i],dp[j-1]+1)

3、初始化

都初始化为整型最大值,否则最后dp表里都是0会影响结果

4、填表顺序

dp[i][j]会用到dp[i+1][j-1],所以必须要从下往上填 , 左右顺序不重要

5、返回值

dp[n-1]

class Solution {
public:
    int minCut(string s) 
    {
     int n=s.size();
     vector<vector<bool>> ispal(n,vector<bool>(n));
     for(int i=n-1;i>=0;--i)
       for(int j=i;j<n;++j) //右上角部分
         if(s[i]==s[j])   ispal[i][j]=i+1<j?ispal[i+1][j-1]:true;
    //第二次枚举 尝试去分割 
    vector<int> dp(n,INT_MAX);//初始化为无穷大  
    for(int i=0;i<n;++i)
        //先看看左边的部分
        if(ispal[0][i]) dp[i]=0;
        else
            for(int j=1;j<=i;++j)//去看看左边 要怎么切割 左开右闭
              if(ispal[j][i]) dp[i]=min(dp[i],dp[j-1]+1);//j代表最后一个回文串的起始位置
         return dp[n-1];
    }
};

五、分割回文子串III(经典)

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示对于字符串的前i个字符,将他分割成j个子串,所需修改的最少字符数

 2、状态转移方程

int cost(string&s,int l,int r) 表示从s的i-j位置,变成回文串所需要的最小修改次数

dp[i][j]:  

(1)j==1(没有分割)  cost(s,0,i-1)

(2)j>1——>min(dp[i][j],dp[m][j-1]+cost(s,m,i-1))

3、初始化

初始化成INT_MAX 确保不影响最终结果 dp[0][0]=0 确保不影响结果

4、填表顺序

上到下,左到右

5、返回值

dp[n][k]

class Solution {
public:
    int palindromePartition(string s, int k) {
    //dp[i][j]表示对于字符串的前i个字符,将他分割成j个子串,所需修改的最少字符数
    int n=s.size();
    vector<vector<int>> dp(n+1,vector<int>(k+1,INT_MAX));
    dp[0][0]=0;
    for(int i=1;i<=n;++i)
      for(int j=1;j<=min(k,i);++j)
         if(j==1) dp[i][j]=cost(s,0,i-1);
         else 
            for(int m=j-1;m<i;++m) dp[i][j]=min(dp[i][j],dp[m][j-1]+cost(s,m,i-1));
            //找前面的状态 0->i  分成j个
            //dp0->m+ cost m->i

       return dp[n][k];//0->n  k
    }

    int cost(string&s,int l,int r)
    {
        int ret=0;
        for(int i=l,j=r;i<j;++i,--j)
         if(s[i]!=s[j]) ++ret;//需要修改一个才能成为回文
       return ret;
    }
};

六、分割回文串IV

 . - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示s字符串[i,j]的子串是否是回文串(i<=j)只需处理右上区即可

 2、状态转移方程

dp[i][j]:  

(1)s[i]!=s[j]——>false

(2)s[i]==s[j]——>

      i==j  true 

      i+1==j   true

      dp[i+1][j-1]

3、初始化

无需初始化

4、填表顺序

dp[i][j]会用到dp[i+1][j-1],所以必须要从下往上填 , 左右顺序不重要

5、返回值

第二次枚举,先固定第一个位置,然后固定第二个位置,看看由两个位置分割出来的三个区域是否都为true

class Solution {
public:
    bool checkPartitioning(string s) {
       //将结果存到dp表中
    int n=s.size();
    vector<vector<bool>> dp(n,vector<bool>(n));
    for(int i=n-1;i>=0;--i)
      for(int j=i;j<n;++j) //右上角部分
         if(s[i]==s[j])   dp[i][j]=i+1<j?dp[i+1][j-1]:true;
     //第二次枚举 先固定第一个,然后固定第二个,然后看看3个是不是都是true即可
     for(int i=1;i<n-1;++i)
       for(int j=i;j<n-1;++j)
         if(dp[0][i-1]&&dp[i][j]&&dp[j+1][n-1]) return true;
    return false;
    }
};

七、不重叠回文子字符串的最大数目

. - 力扣(LeetCode)

class Solution {
public:
    int maxPalindromes(string s, int k) {
        //dp[i]表示0->i中的不重叠回文子字符串的最大数目
        int n=s.size();
        vector<int> dp(n+1);
        //如果s[i]不在回文串中 dp[i+1]=dp[i]
        //如果s[r]在回文串中,采用中心扩展,l->r是回文子串,且r-l+1>=k 有dp[i]=max(dp[i],dp[l-1]+1)
        for(int i=0;i<n*2-1;++i)
        {
            //两边到中间不适合判断长度,应该从中间到两边
          int l=i/2,r=l+i%2; //中心扩展判断是否回文
          dp[l+1]=max(dp[l],dp[l+1]);
          for(;l>=0&&r<n&&s[l]==s[r];--l,++r)
             if(r-l+1>=k)
             {
                dp[r+1]=max(dp[r+1],dp[l]+1);
                break;
             }
        }
        return dp[n];
    }
};

八、最长回文子序列

. - 力扣(LeetCode)

class Solution {
public:
    int longestPalindromeSubseq(string s) 
    {
       //子序列和子串的区别就是可以不连续
       int n=s.size();
       vector<vector<int>> dp(n,vector<int>(n));//只会用到右上半部分
       for(int i=n-1;i>=0;--i)
       {
        //dp[i][j]表示i-j区间内所有子序列中,最长回文子序列的长度
          dp[i][i]=1;
          for(int j=i+1;j<n;++j)
            if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2; //i+1=j的情况可以不用考虑 
            //虽然会出现用不到的格子,但是里面是0所以不会影响计算结果
            else dp[i][j]=max(dp[i+1][j],dp[i][j-1]);
       }
       return dp[0][n-1];
    }
};

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示s字符串[i,j]所有子序列中的最长子序列的长度

 2、状态转移方程

dp[i][j]:  

(1)s[i]!=s[j]——>max(dp[i,j-1],dp[i+1][j])

(2)s[i]==s[j]——>

      i==j  1

      i+1==j   2

      dp[i+1][j-1]+2

3、初始化

初始化为0  dp[i][i]=1

4、填表顺序

上到下,左到右

5、返回值

dp[0][n-1]

九、让字符串成为回文串的最小插入次数

. - 力扣(LeetCode)

算法原理:

1、状态表示(经验+题目要求)

dp[i][j]表示s字符串[i,j]子串,使他成为回文子串的最小插入次数

 2、状态转移方程

dp[i][j]:  

(1)s[i]!=s[j]——>min(dp[i,j-1],dp[i+1][j])+1

(2)s[i]==s[j]——>

      i==j  0

      i+1==j   0

      dp[i+1][j-1]

3、初始化

初始化为0 

4、填表顺序

下往上,左到右

5、返回值

dp[0][n-1]

class Solution {
public:
    int minInsertions(string s) {
       int n=s.size();
       vector<vector<int>> dp(n,vector<int>(n));
       for(int i=n-1;i>=0;--i)
         for(int j=i+1;j<n;++j)
           if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];
           else dp[i][j]=min(dp[i][j-1],dp[i+1][j])+1;
       return dp[0][n-1];
    }
};

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1807287.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小冬瓜AIGC 手撕LLM 拼课

小冬瓜aigc手撕LLM学习 官方认证 手撕LLMRLHF速成班-(附赠LLM加速分布式训练超长文档&#xff09; 帮助多名同学上岸LLM方向&#xff0c;包括高校副教授&#xff0c;北美PhD&#xff0c;大厂等 课程名称【手撕LLMRLHF】 授课形式&#xff1a;在线会议直播讲解课后录播 时间&…

Nvidia的成功与竞争:CEO黄仁勋的自信与挑战

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【将xml文件转yolov5训练数据txt标签文件】连classes.txt都可以生成

将xml文件转yolov5训练数据txt标签文件 前言一、代码解析 二、使用方法总结 前言 找遍全网&#xff0c;我觉得写得最详细的就是这个博文⇨将xml文件转yolov5训练数据txt标签文件 虽然我还是没有跑成功。那个正则表达式我不会改QWQ&#xff0c;但是不妨碍我会训练ai。 最终成功…

LangChain基础知识入门

LangChain的介绍和入门 1 什么是LangChain LangChain由 Harrison Chase 创建于2022年10月&#xff0c;它是围绕LLMs&#xff08;大语言模型&#xff09;建立的一个框架&#xff0c;LLMs使用机器学习算法和海量数据来分析和理解自然语言&#xff0c;GPT3.5、GPT4是LLMs最先进的代…

架构设计-用户信息及用户相关的密码信息设计

将用户的基本信息和用户密码存放在不同的数据库表中是一种常见的安全做法&#xff0c;这种做法旨在增强数据的安全性和管理的灵活性。以下是这种做法的几个关键原因&#xff1a; 安全性增强&#xff1a; 当用户密码被单独存放在一个表中时&#xff0c;可以使用更强大的加密和哈…

kafka集成SpringBoot api编写教程

1.新建项目 用的idea是20222.1.3版本&#xff0c;没有Spring Initializr 插件&#xff0c;不能直接创建springboot项目 可以在以下网址创建项目&#xff0c;下载后解压&#xff0c;然后用idea打开项目即可 1.1 在 https://start.spring.io/ 上创建项目 1.2上传到linux&#x…

C语言 | Leetcode C语言题解之第140题单词拆分II

题目&#xff1a; 题解&#xff1a; struct Trie {int ch[26];bool flag; } trie[10001];int size;void insert(char* s, int sSize) {int add 0;for (int i 0; i < sSize; i) {int x s[i] - a;if (trie[add].ch[x] 0) {trie[add].ch[x] size;memset(trie[size].ch, 0…

读AI未来进行式笔记07量子计算

1. AI审讯技术 1.1. 发明者最初的目的是发明一种能够替代精神药物&#xff0c;为人类带来终极快乐的技术 1.1.1. 遗憾的是&#xff0c;他找到的只是通往反方向的大门 1.2. 通过非侵入式的神经电磁干扰大脑边缘系统&#xff0c;诱发受审者最…

配置 JDK 和 Android SDK

目录 一、配置JDK 1. 安装 JDK 2. JDK 环境配置 3. JDK的配置验证 二、配置 adb 和Android SDK环境 1、下载 2、配置 Android SDK 环境 一、配置JDK 1. 安装 JDK 安装链接&#xff1a;Java Downloads | Oracle 我安装的是 .zip &#xff0c;直接在指定的文件夹下解压就…

[沉迷理论]进制链表树

往期文章推荐&#xff1a; 题解之最大子矩阵-CSDN博客 洛谷P1115最大子段和[神奇的题目]-CSDN博客 &#xff08;一条神奇的分割线&#xff09; 前言 好久没有更新的我总算在百忙之中抽出时间写了篇博客。 最近总算结束了动态规划的学习&#xff0c;真的是头昏脑涨啊。 最…

论文阅读——MIRNet

项目地址&#xff1a; GitHub - swz30/MIRNet: [ECCV 2020] Learning Enriched Features for Real Image Restoration and Enhancement. SOTA results for image denoising, super-resolution, and image enhancement.GitHub - soumik12345/MIRNet: Tensorflow implementation…

【云岚到家】-day02-1-区域服务后续开发及完善

【云岚到家】-day02-1-区域服务后续开发及完善 1 区域服务后续开发1.1 添加区域服务1.1.1 接口定义1.1.1.1 接口设计1.1.1.2 接口定义-json 1.1.2 接口开发1.1.2.1 mapper1.1.2.2 service1.1.2.3 controller 1.1.3 测试 1.2 修改价格1.2.1 接口定义1.2.1.1 接口设计1.2.1.2 接口…

【RAG入门教程01】Langchian框架 v0.2介绍

LangChain 是一个开源框架&#xff0c;旨在简化使用大型语言模型 (LLM) 创建应用程序的过程。可以将其想象成一套使用高级语言工具进行搭建的乐高积木。 它对于想要构建复杂的基于语言的应用程序而又不必管理直接与语言模型交互的复杂性的开发人员特别有用。它简化了将这些模型…

动态规划(多重背包+完全背包)

P2851 [USACO06DEC] 最少的硬币 G 题解&#xff1a;从题目上看到那个有n种不同的货币&#xff0c;对于买家来说每个货币有C[ i ]个&#xff0c;是有限个数的&#xff0c;但是对于卖家来说 每个货币都是无限的&#xff0c;题目中要我们求的是买到这个物品的最小交易的货币数&…

电子设计入门教程硬件篇之集成电路IC(二)

前言&#xff1a;本文为手把手教学的电子设计入门教程硬件类的博客&#xff0c;该博客侧重针对电子设计中的硬件电路进行介绍。本篇博客将根据电子设计实战中的情况去详细讲解集成电路IC&#xff0c;这些集成电路IC包括&#xff1a;逻辑门芯片、运算放大器与电子零件。电子设计…

燃料电池汽车践行者

前言 见《氢燃料电池技术综述》 见《燃料电池工作原理详解》 见《燃料电池发电系统详解》 见《燃料电池电动汽车详解》 见《氢燃料电池汽车行业发展》 现代汽车&#xff08;中国&#xff09; 现代汽车集团&#xff0c;自1998年成立氢燃料电池研发小组以来深耕氢燃料电池技术&am…

计算机操作系统基础知识:操作系统体系结构图,操作系统的内核,大内核与微内核的区别和优缺点,时钟管理,原语

1.操作系统体系结构图&#xff1a; 2.操作系统的内核&#xff1a; 时钟管理&#xff1a;利用时钟中断实现计时功能。 原语&#xff1a;原语是一种特殊的程序&#xff0c;具有原子性。也就是说&#xff0c;这段程序运行必须一气呵成&#xff0c;不能被中断。 ubuntu、centos的…

VUE3 学习笔记(13):VUE3 下的Element-Plus基本使用

UI是页面的门面&#xff0c;一个好的UI自然令人赏心悦目&#xff1b;国人团队开发的ElementUI在众多UI中较为常见&#xff0c;因此通过介绍它的使用让大家更好的了解第三方UI的使用。 安装 Npm install element-plus --save 或 Cnpm install element-plus --save 配置 全局配置…

Vitis HLS 学习笔记--循环边界包含变量

目录 1. 简介 2. 分析与详解 2.1 未优化 2.2 LOOP_TRIPCOUNT 优化指令 2.3 重写变量循环边界 3. 总结 1. 简介 在硬件设计中&#xff0c;循环的迭代次数通常需要是固定的&#xff0c;因为这有助于资源的预分配和时序分析。 循环边界包含变量意味着循环的迭代次数不是固…

MySQL之多表查询—表子查询

一、引言 上一篇博客学习了行子查询。&#xff08;看弹幕&#xff1a;同一张表用or,不同张表用union&#xff09; 下面接着学习子查询当中的最后一种——表子查询。 表子查询 1、概念 子查询返回的结果是多行多列&#xff0c;这种子查询称为表子查询。 2、常用的操作符 IN 3、…