LeetCode5.最长回文子串

news2024/11/18 11:34:51

 昨天和之前打比赛的队友聊天,他说他面百度面到这道算法题,然后他用暴力法解的,面试官让他优化他没优化出来,这道题我之前没写过,我就想看看我能不能用效率高一点的方法把它做出来,我一开始就在想用递归或者翻转字符串等等技巧,想了半个多小时都想不到,然后算了,我也用暴力法吧,然后就写了如下代码:

class Solution {
    public String longestPalindrome(String s) {
       int n = s.length();
       String ans = "";
       for(int i = 0;i<n;i++){
           int l = i, r=i;
           while(l<= r && l >=0 && r < n && s.charAt(l) == s.charAt(r)){
              String tem =s.substring(l,r+1);
              if(r-l+1 > ans.length())ans=tem;
              l--;r++;
           }
           l = i;r=l+1;
           while(l >=0 && r < n && s.charAt(l) == s.charAt(r)){
              String tem =s.substring(l,r+1);
              if(r-l+1 > ans.length())ans=tem;
              l--;r++;
           }
       }
       return ans;
    }

}

这个暴力法就非常简单了,就是遍历字符的每个字符,然后以这个字符为中心用左右指针往两边移动,然后是写了两个while,一个是左右指针指向同一个字符然后向左右移动,还有一个是左右指针指向相邻的字符向两边移动。

还是看看官方题解的做法吧。

题解的方法一是用的动态规划:

class Solution {
    public String longestPalindrome(String s) {
       int len = s.length();
       if(len < 2)return s;
       boolean[][] dp = new boolean[len][len];
       for(int i=0;i<len;i++){
           dp[i][i] = true;
       }
       int begin =0;
       int maxLen =1;
       char[] str = s.toCharArray();
       for(int L =2;L<=len;L++){
           for(int i=0;i<len;i++){
               int j = i+L-1;
               if(j>=len){
                   break;
               }else{
                   if(str[i] != str[j]){
                       dp[i][j] = false;
                   }else{
                       if(j-i<3){
                           dp[i][j] = true;
                       }else{
                           dp[i][j] = dp[i+1][j-1];
                       }
                   }
               }
            if(dp[i][j] && j-i+1 > maxLen){
                maxLen = j-i+1;
                begin = i;
            }
           }
       }
       
       return s.substring(begin, begin+maxLen);
    }

}

dp[i][j]表示s的第i个字符到第j个字符这一个子串是不是一个回文字符串。

首先,初始状态:dp[i][i] = true;

然后,状态转移方程:dp[i][j] = ?

第一种情况,s.charAt(i) != s.charAt(j), 那么dp[i][j] = false;

第二种情况,s.charAt(i) == s.charAt(j),如果子串长度小于3,那么dp[i][j]=true;否则dp[i][j] = dp[i-1][j+1]

只要找出dp[i][j]中为true且长度最大的那个就行,这一步在填充dp的数组的同时进行,不用另外遍历,只要一个max动态更新就行。然后值得注意的是这里是把字符串转成了字符数组,因为我们需要频繁的找字符串中的字符,用数组效率更高,用charAt方法需要遍历字符串,而数组可以直接通过寻址公式得出。

题解的第二种方法用的是中心扩展,和我的方法差不多,只是它把while循环封装成了函数而已,以下是题解方法二代码:

class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) {
            return "";
        }
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    public int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }
        return right - left - 1;
    }
}

题解的方法三就比较高级了,时间复杂度只有O(n),叫Manacher算法:

定义一个概念”臂长“,如果一个回文字符串的长度是2*lenght+1,那么这个回文字符串的臂长就是length。

如果位置j的臂长为length那么如何找到位置i的臂长呢?

对于奇数长度的回文字符串而言:

如果i关于j的对称点2*j-i的臂长是n,那么位置i的臂长是min(n,2*j-i)。这个其实很好理解,因为j左右length拿一段是对称的,所以如果2*j-i有n的臂长按道理i也是这么大的臂长,但是只有length这一部分是对称的,而2*j-i的对称部分还可以向两边扩展,所以i的臂长只能取n和2*j-i的最小值。

偶数长度的回文字符串呢?

我们通过在字符串的两头和没两个字符之间加上#,这样无论奇数还是偶数长度的回文字符串都变成了奇数长度的回文字符串,比如aaba处理成#a#a#b#a#,偶数长度的回文字符串aa变成了#a#a#。aba还是变成奇数#a#b#a#。需要注意的是这里添加的#需要是字符串里面没有出现过的。最后记得把回文字符还原,把#去掉。以下是Manacher方法的代码:

class Solution {
    public String longestPalindrome(String s) {
        int start = 0, end = -1;
        StringBuffer t = new StringBuffer("#");
        for (int i = 0; i < s.length(); ++i) {
            t.append(s.charAt(i));
            t.append('#');
        }
        t.append('#');
        s = t.toString();

        List<Integer> arm_len = new ArrayList<Integer>();
        int right = -1, j = -1;
        for (int i = 0; i < s.length(); ++i) {
            int cur_arm_len;
            if (right >= i) {
                int i_sym = j * 2 - i;
                int min_arm_len = Math.min(arm_len.get(i_sym), right - i);
                cur_arm_len = expand(s, i - min_arm_len, i + min_arm_len);
            } else {
                cur_arm_len = expand(s, i, i);
            }
            arm_len.add(cur_arm_len);
            if (i + cur_arm_len > right) {
                j = i;
                right = i + cur_arm_len;
            }
            if (cur_arm_len * 2 + 1 > end - start) {
                start = i - cur_arm_len;
                end = i + cur_arm_len;
            }
        }

        StringBuffer ans = new StringBuffer();
        for (int i = start; i <= end; ++i) {
            if (s.charAt(i) != '#') {
                ans.append(s.charAt(i));
            }
        }
        return ans.toString();
    }

    public int expand(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            --left;
            ++right;
        }
        return (right - left - 2) / 2;
    }
}

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

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

相关文章

排程系统中关于任务优先级的需求延伸与设计构思

无论是面向销售订单的MPS&#xff0c;还是基于多工序制约关系的APS&#xff0c;还是具体车间生产中针对单一工序的任务作业调度优化&#xff0c;都存在基于被排程对象(例如销售订单、生产工单、工序任务)的优先级进行优化的需求场景。当我们仅在宏观、较高层次的角度考虑&#…

Linux高级管理-基于域名的虚拟Web主机搭建

客服机限制地址 通过 Require 配置项&#xff0c;可以根据主机的主机名或P地址来决定是否允许客户端访问。在httpd服 务器的主配置文件的<Location>&#xff0c;<Directory>、<Files>、<Limit>配置段中均可以使用Require 配置 项来控制客户端的访问。使…

Python常见面试知识总结(二):数据结构、类方法及异常处理

【十三】Python中assert的作用&#xff1f; Python中assert&#xff08;断言&#xff09;用于判断一个表达式&#xff0c;在表达式条件为 f a l s e false false的时候触发异常。 断言可以在条件不满足程序运行的情况下直接返回错误&#xff0c;而不必等待程序运行后出现崩溃…

openGauss学习笔记-153 openGauss 数据库运维-备份与恢复-物理备份与恢复之gs_probackup

文章目录 openGauss学习笔记-153 openGauss 数据库运维-备份与恢复-物理备份与恢复之gs_probackup153.1 背景信息153.2 前提条件153.3 限制说明153.4 命令说明153.5 参数说明153.6 备份流程153.7 故障处理 openGauss学习笔记-153 openGauss 数据库运维-备份与恢复-物理备份与恢…

java.net.SocketException: Connection reset

背景 在我用socket进行TCP通信的时候&#xff0c;当我关闭client端时在服务端出现了Connection reset的异常。 一、问题 下面是异常信息&#xff1a; Exception in thread "Thread-12" java.lang.RuntimeException: java.net.SocketException: Connection reseta…

【复现】AnimateDiff复现过程记录

出现了非常多的问题&#xff0c;痛苦 1.首先是下载基础模型 手动下载所有模型并创建目录放到对应的地方 文件路径问题是最简单也是最麻烦的&#xff0c;简单是只要把原来指向huggingface和openai之类要联网下载的路径改为存放文件的本地路径就行&#xff0c;一定要是绝对路径&…

【计算机网络】UDP报文详解

目录 一. UDP协议概述 二. UDP报文格式 首部 三. UDP的缓冲区 一. UDP协议概述 UDP——用户数据报协议&#xff0c;是传输层的一个重要协议 基于UDP的应用层协议有&#xff1a;DNS&#xff0c;TFTP&#xff0c;SNMP&#xff0c;NTP 协议全称默认端口号DNSDomain Name Se…

idea__SpringBoot微服务09——员工管理系统,(Springboot解决乱码),thymeleaf语法,404页面。

员工管理系统 完整项目地址&#xff1a;一、首页实现&#xff08;注意的点&#xff09;二、国际化三、乱码解决四、登录功能实现&#xff08;注意的点&#xff09;五、登录拦截器&#xff08;注意的点&#xff09;六、展示员工列表&#xff08;注意的点&#xff09;1、前端页面…

HCIA-H12-811题目解析(9)

1、【单选题】下面选项中&#xff0c;能使一台IP地址为10.0.0.1的主机访问Interne的必要技术是&#xff1f; 2、【单选题】 FTP协议控制平面使用的端口号为&#xff1f; 3、【单选题】 使用FTP进行文件传输时&#xff0c;会建立多少个TCP连接&#xff1f; 4、【单选题】完成…

18.Java程序设计-基于Springboot的电影院售票系统的设计与实现

摘要 电影产业在当今社会中占据着重要地位&#xff0c;电影院作为观影的主要场所&#xff0c;其售票系统的高效性和用户体验至关重要。本文基于Spring Boot框架设计并实现了一款电影院售票系统&#xff0c;旨在提高售票效率、优化用户体验&#xff0c;并解决传统售票方式存在的…

堪比Postman!实用IDEA插件推荐

Postman是大家最常用的API调试工具&#xff0c;那么有没有一种方法可以不用手动写入接口到Postman&#xff0c;即可进行接口调试操作&#xff1f;今天给大家推荐一款IDEA插件&#xff1a;Apipost Helper&#xff0c;写完代码就可以调试接口并一键生成接口文档&#xff01;而且还…

我的NPI项目之Android 显示 -- 背光的电路小知识

由于使用的高通平台一直在演化&#xff0c;从SDM660,QCM4290,QCM4490再到QCM6490。产品的背光设计也是一直在迭代。 简单罗列了一下所经历的一些设计&#xff0c;简单的背光也涉及到了很多学问。 先说有哪些类型&#xff1a; 1. SDM660上由PMIC提供了wled给背光, 透过驱动直…

数据结构与算法-Rust 版读书笔记-2线性数据结构-双端队列

数据结构与算法-Rust 版读书笔记-2线性数据结构-双端队列 1、双端队列 deque又称为双端队列&#xff0c;双端队列是与队列类似的项的有序集合。deque有两个端部&#xff1a;首端和尾端。deque不同于队列的地方就在于项的添加和删除是不受限制的&#xff0c;既可以从首尾两端添…

记一次测试环境git翻车经历

本来想拉一个功能分支进行新的功能开发&#xff0c;合并代码发现没有冲突居然有文件被修改了&#xff0c;贸然选择最近的一次回滚提交&#xff0c;没想到不假思索的push -f 导致一部分dev主干的代码不见了。 事故记录 开发分支origin/dev&#xff0c;功能分支file 合并之后发…

金额格式化,利率格式化

<el-inputplaceholder"请输入"size"medium"v-model"amt"maxlength"16":disabled"showBtn no || readOnly"oninput"this.value this.value.replace(/[^\d.]/g,).replace(/\.{2,}/g, .).replace(/^0(\d)/, $1) // 第…

Android--Jetpack--Databinding源码解析

慢品人间烟火色&#xff0c;闲观万事岁月长 一&#xff0c;基本使用 关于databinding的基本使用请看之前的文章 Android--Jetpack--Databinding详解-CSDN博客 二&#xff0c;xml布局解析 分析源码呢&#xff0c;主要就是从两方面入手&#xff0c;一个是使用&#xff0c;一个…

Nginx正则表达式

目录 1.nginx常用的正则表达式 2.location location 大致可以分为三类 location 常用的匹配规则 location 优先级 location 示例说明 优先级总结 3.rewrite rewrite功能 rewrite跳转实现 rewrite执行顺序 语法格式 rewrite示例 实例1&#xff1a; 实例2&#xf…

Vue3封装一个轮播图组件

先看效果 编写组件代码 CarouselChart.vue <template><div classimg-box><el-button clickpreviousImages v-ifprops.showBtn>←</el-button><div classimg><div styledisplay: flex;gap: 20px idmove><imgclassimg-item v-for(item…

hdlbits系列verilog解答(Ringer)-55

文章目录 一、问题描述二、verilog源码三、仿真结果 一、问题描述 本次我们设计一个电路以实现对手机铃声和振动的控制。当工作在振动模式时&#xff0c;开启振动&#xff0c;否则开启铃声。 尝试只使用assign语句&#xff0c;测试一下你是否能将描述转化成数字逻辑电路。 二…

Guava反射工具详解

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;今天咱们聊聊Java反射&#xff0c;特别是在Guava这个强大的库中&#xff0c;它是怎么让反射变得更简单&#xff0c;更有趣的。咱们都知道&#xff0c;反射在Java中是个相当强大的特性&#xff0c;它允许程序在运…