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

LeetCode0005.最长回文子串 Go语言AC笔记

 时间复杂度:O(n²)

解题思路

动态规划经典题型!

该题可以用多种方法解决,即使用动态规划解决也有多种方法,但我用的这个动态规划解法绝对是最容易理解的!

令dp[i][j]表示S[i]至S[j]所表示的子串是否是回文子串,是则为true,不是则为false。这样根据S[i]是否等于S[j],可以把转移情况分为两类:

  1. 若S[i]==S[j],那么只要S[i+1]至S[j-1]是回文子串,S[i]至S[j]就是回文子串;如果S[i+1]至S[j-1]不是回文子串,则S[i]至S[j]也不是回文子串。
  2. 若S[i]!=S[j],那么S[i]至S[j]一定不是回文子串。

由此可以写出状态转移方程:

dp[i][j]= \begin{cases} &dp[i+1][j-1], \text{ } S[i]==S[j] \\ & 0,\text{ } S[i]!=S[j] \end{cases}

边界:dp[i][j]=1,dp[i][i+1]=(S[i]==S[i+1])?true:false。

到这里还有一个问题没有解决,那就是如果按照i和j从小到大的顺序来枚举子串的两个端点,然后更新dp[i][j],会无法保证dp[i+1][j-1]已经被计算过,从而无法得到正确的dp[i][j]。

比如下面这个例子:

 如上图所示,先固定i==0,然后枚举j从2开始。当求解dp[0][2]时,精会转换为dp[1][1],而dp[1][1]是在初始化中得到的;当求解dp[0][3]时,将会转换为dp[1][2],而dp[1][2]也是在初始化中得到的;当求解dp[0][4]时,将会转换为dp[1][3],但是dp[1][3]并不是已经计算过的值,因此无法状态转移。事实上,无论对i和jd枚举顺序做何调整,都无法调和这个矛盾,因此必须想办法寻找新的枚举方式。

根据递推写法从边界出发的原理,注意到边界表示的是长度为1和2的子串,且每次转移时都对子串的长度减了1,因此不妨考虑按子串的长度和子串的初始位置进行枚举,即第一遍将长度为3的子串的dp值全部求出,第二遍通过第一遍结果计算出长度为4的子串的dp值……这样就可以避免状态无法转移的问题。

如上图所示,可以先枚举子串长度L(注意:L是可以取到整个字符串的长度len(S)的),再枚举左端点i,这样右端点i+L-1也可以直接得到。

AC代码

func longestPalindrome(s string) string {
    res:=s[0:1]
    n:=len(s)
    dp:=make([][]bool,n)
    for i:=0;i<len(dp);i++{
        dp[i]=make([]bool,n)
    }
    //创建边界条件
    for i:=0;i<n-1;i++{
        dp[i][i]=true
        //长度为2的回文子串
        if s[i]==s[i+1]{
            dp[i][i+1]=true
            if len(res)==1{
                res=s[i:i+2]
            }
        }
    }
    //从长度为3的回文子串开始
    for l:=3;l<=n;l++{
        //左端点为i
        for i:=0;i+l-1<n;i++{
            j:=i+l-1//子串右端点
            if s[i]==s[j]&&dp[i+1][j-1]{
                dp[i][j]=true
                if l>len(res){
                    res=s[i:j+1]
                }
            }
        }
    }
    return res
}

感悟

看了官方题解,发现效率越高的方法越难以理解,保险起见还是会个最low的动态规划应付面试吧……

相关文章:

  • JavaScript:JavaScript编程语言学习之基础知识(变量/类型/数组/运算符/标签函数对象)的简介、案例应用之详细攻略
  • TiDB 重要监控指标详解
  • 【web前端开发】前端生日礼物--注册页面篇
  • C++ Reference: Standard C++ Library reference: C Library: cmath
  • 康耐视InSight相机与西门子PLC关于Profinet通讯说明
  • JDK19新特性使用详解
  • 聊聊如何制作自定义ArcGIS Python工具箱
  • 数字图像处理-对比度调整背景相减
  • HTTP协议3)----对于网络层的详细讲解
  • [单片机框架][device层] charger 电源管理
  • [单片机框架][drivers层][bq25601] charger 电源管理
  • Java多线程--InheritableThreadLocal--使用/实例
  • java计算机毕业设计宿迁学院学生设计作品交流网站源代码+数据库+系统+lw文档
  • ByteTrack:通过关联每个检测框进行多对象跟踪
  • 新能源汽车行业资讯-2022-9-22
  • 4个实用的微服务测试策略
  • export和import的用法总结
  • java2019面试题北京
  • MQ框架的比较
  • nfs客户端进程变D,延伸linux的lock
  • Shell编程
  • 构建二叉树进行数值数组的去重及优化
  • 机器学习学习笔记一
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 排序算法学习笔记
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 前端性能优化——回流与重绘
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 三栏布局总结
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 【运维趟坑回忆录】vpc迁移 - 吃螃蟹之路
  • Hibernate主键生成策略及选择
  • NLPIR智能语义技术让大数据挖掘更简单
  • ​LeetCode解法汇总1410. HTML 实体解析器
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • (06)Hive——正则表达式
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • ..回顾17,展望18
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • @reference注解_Dubbo配置参考手册之dubbo:reference
  • @Resource和@Autowired的区别
  • [ vulhub漏洞复现篇 ] GhostScript 沙箱绕过(任意命令执行)漏洞CVE-2019-6116
  • [1]-基于图搜索的路径规划基础
  • [20171102]视图v$session中process字段含义
  • [AIGC] Nacos:一个简单 yet powerful 的配置中心和服务注册中心
  • [ai笔记3] ai春晚观后感-谈谈ai与艺术
  • [Android]使用Android打包Unity工程
  • [C puzzle book] types
  • [C/C++随笔] char与unsigned char区别
  • [C\C++]读入优化【技巧】
  • [C]整形提升(转载)