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

代码随想录算法训练营第四十六天|回文子串专题: 647. 回文子串、516.最长回文子序列

1、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。

 所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的

// 动态规划的思路
// func countSubstrings(s string) int {
//     if len(s) == 0 {
//         return 0
//     }
//     result := 0
//     dp := make([][]bool, len(s))
//     for i := 0; i < len(s); i++ {
//         dp[i] = make([]bool, len(s))
//     }
//     for i := len(s) - 1; i >= 0; i-- {  // [i, j]区间内的子串是否是回文子串
//         for j := i; j < len(s); j++ {
//             if s[i] == s[j] {
//                 if j - i <= 1 {
//                     result++
//                     dp[i][j] = true
//                 }else if dp[i + 1][j - 1] {
//                     result++
//                     dp[i][j] = true
//                 }
//             }
//         }
//     }
//     return result
// }


// 中心扩展法,双指针
func countSubstrings(s string) int {
    if len(s) == 0 {
        return 0
    }
    result := 0
    for i := 0; i < len(s); i++ {
        // 子串长度奇数个
        for left, right := i, i; left >= 0 && right < len(s) && s[left] == s[right]; {
            result++
            left--
            right++
        }
        // 子串长度偶数个
        for left, right := i, i + 1; left >= 0 && right < len(s) && s[left] == s[right]; {
            result++
            left--
            right++
        } 
    }
    return result
}

516.最长回文子序列

回文子串是要连续的,回文子序列可不是连续的!

这是不连续的

  1. 确定dp数组(dp table)以及下标的含义

dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]

  1. 确定递推公式

在判断回文子串的题目中,关键逻辑就是看s[i]与s[j]是否相同。

如果s[i]与s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;

如图: 

516.最长回文子序列

(如果这里看不懂,回忆一下dp[i][j]的定义)

如果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]);

从递归公式中,可以看出,dp[i][j] 依赖于 dp[i + 1][j - 1] ,dp[i + 1][j] 和 dp[i][j - 1],如图:

所以遍历i的时候一定要从下到上遍历,这样才能保证下一行的数据是经过计算的

j的话,可以正常从左向右遍历。

最后的结果返回一定是dp[0][len(s) - 1],表示区间【0, len(s) - 1】

func longestPalindromeSubseq(s string) int {
    if len(s) <= 1 {
        return len(s)
    }
    dp := make([][]int, len(s))
    // dp数组表示[i, j]区间之间的最长回文子序列长度
    for i := 0; i < len(s); i++ {
        dp[i] = make([]int, len(s))
    }
    for i := 0; i < len(s); i++ {
        dp[i][i] = 1
    }
    for i := len(s) - 1; i >= 0; i-- {
        for j := i + 1; j < len(s); j++ {
            if s[i] == s[j] {
                dp[i][j] = dp[i + 1][j - 1] + 2
            }else {
                dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
            }
        }
    }
    return dp[0][len(s) - 1]
    // 区间[0, len(s) - 1]
}

http://www.dtcms.com/a/98254.html

相关文章:

  • DSP+AI综合应用案例1——三种波形识别(预告)
  • java网盘项目 目前 文件有分享表 文件夹也要创建分享表吗
  • 伯恩茅斯 vs 曼城
  • Java 输入流到输出流
  • 开发过程中需要安装的浏览器插件
  • 【学Rust写CAD】17 通用2D仿射变换矩阵结构体(matrix/generic.rs)
  • keil中文注释出现乱码怎么解决
  • Selenium自动化测试中常见报错类型及解决方案
  • 【Qt】三种操作sqlite3的方式及其三种多表连接
  • 总结PostgreSQL创建数据库失败的解决办法
  • SQL语句及其应用(中)(DQL语句之单表查询)
  • 机器学习的一百个概念(1)单位归一化
  • java项目之基于ssm的乡镇自来水收费系统(源码+文档)
  • 签名计算:对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)【java版本、JS(Vue)、iOS版本的实现】
  • LocalDateTime序列化总结
  • 接龙数列(最长上升 动规)
  • 第十六章 常用算法
  • 自动化与智能化的认知差异
  • 【即插即用涨点模块-卷积】SPDConv空间深度卷积,助力小目标与低分辨有效涨点【附源码+注释】
  • 28_跨域
  • Android设计模式之模板方法模式
  • 如何在 AI 搜索引擎(GEO)霸屏曝光,快速提升知名度?
  • RabbitMQ高级特性--重试特性
  • 【C++初阶】第12课—list
  • 人工智能入门(1)
  • 字符和字符串的输入方式
  • 安当SLAc操作系统安全登录解决方案:破解设备供应商Windows权限失控困局
  • 全自动数字网络机器人:重塑未来的无形引擎 ——从金融量化到万物互联,为何必须“ALL IN”?
  • MySQL 中的表连接(JOIN)详解
  • 六十天前端强化训练之第三十五天之Jest单元测试大师课:从入门到实战