LeetCode5.最长回文子串

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

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.rhkb.cn/news/215197.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

31、卷积 - 参数 dilation 以及空洞卷积

在卷积算法中,还有一个不常见的参数叫做dilation(中文:膨胀)。 很多同学可能没听说过这个参数,下面看看这个参数有什么作用,用来控制什么的。 我们还是放这个经典的卷积运算图,图中是看不出 dilation 这个参数的存在的。 如果再换一张图呢,发现两图的区别了吗? 没错…

设计模式篇---代理模式

文章目录 概念结构实例静态代理动态代理 总结 概念 代理模式&#xff1a;给某一个对象提供一个代理或占位符&#xff0c;并由代理对象来控制对原对象的访问。 比如我们想从其他国家买东西&#xff0c;但我们无法直接联系外国的商家&#xff0c;可以找代理商&#xff0c;让他们…

学习人工智能-基础篇

背景 随着大模型的火爆&#xff0c;人工智能再次被推到高潮&#xff0c;其实它在众多行业领域已经落地很多应用&#xff0c;并给社会带来了巨大的经济价值。其中包括互联网、教育、金融、医疗、交通、物流等等。在测试领域也有一些落地的案例&#xff0c;作为测试人员&#xf…

机器学习-逻辑回归

一、引言 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广泛应用于分类问题的监督学习算法。尽管名字中含有“回归”二字&#xff0c;但这并不意味着它用于解决回归问题。相反&#xff0c;逻辑回归专注于解决二元或多元分类问题&#xff0c;如邮件是垃圾邮件还是…

2023-12学习笔记

1.NonNull要手动写无参构造器 这是一个我今天研究了很久的问题&#xff0c;开始不知道原因是在这里&#xff0c;还在那想是不是Data覆盖了无参构造&#xff0c;结果当然不是。先说下解决历程 1.问题起因 通过RequestBody接收前端报文的时候报错&#xff0c;大致是说我构造方…

热电厂发电机组常见故障及预测性维护方法

热电厂的发电机组是关键的能源生产设备&#xff0c;在电力供应中扮演着关键角色。但经过长期运行和高负荷工作&#xff0c;一旦发生故障&#xff0c;可能导致停机、设备损坏甚至引发严重事故。因此&#xff0c;实施有效的预测性维护方法对于确保发电机组的稳定运行至关重要。本…

Python从入门到精通五:Python数据容器

数据容器入门 为什么学习数据容器 思考一个问题&#xff1a;如果我想要在程序中&#xff0c;记录5名学生的信息&#xff0c;如姓名。 如何做呢&#xff1f; 学习数据容器&#xff0c;就是为了批量存储或批量使用多份数据 Python中的数据容器&#xff1a; 一种可以容纳多份…

gitee对接使用

1.创建一个文件夹 2.进入Gitee接受对方项目编辑 3.打开终端初始化一开始创建的文件夹 git init 3.1打开终端 3.2输入git.init 4.克隆对方的项目 4.1进入Gitee复制对方项目的路径 4.2在编辑器终端内克隆对方项目 git clone 网址 如此你的编辑器就会出现对方的项目 …

使用pe安装windows操作系统

一、系统安装前准备工作&#xff0c;制作系统盘 &#xff08;1&#xff09;拷贝电脑上的资料 &#xff08;2&#xff09;准备一个至少8G的U盘 &#xff08;3&#xff09;下载windows镜像文件及pe软件 通过百度网盘可下载下列软件及镜像 windows镜像文件&#xff08;百度网盘…

如何通过SPI控制Peregrine的数控衰减器

概要 Peregrine的数控衰减器PE4312是6位射频数字步进衰减器(DSA,Digital Step Attenuator)工作频率覆盖1MHz~4GHz,插入损耗2dB左右,衰减步进0.5dB,最大衰减量为31.5dB,高达59dBm的IIP3提供了良好的动态性能,切换时间0.5微秒,供电电源2.3V~5.5V,逻辑控制兼容1.8V,20…

容器重启后,Conda文件完整保存(虚拟环境、库包),如何重新安装conda并迁移之前的虚拟环境

Vim安装 容器重启后默认是vi&#xff0c;升级vim&#xff0c;执行命令 apt install -y vim安装 Anaconda 1. 下载Anaconda 其他版本请查看Anaconda官方库 wget https://mirrors.bfsu.edu.cn/anaconda/archive/Anaconda3-2023.03-1-Linux-x86_64.sh --no-check-certificate…

HarmonyOS4.0从零开始的开发教程09页签切换

HarmonyOS&#xff08;七&#xff09;页签切换 List组件和Grid组件的使用 Tabs组件的使用 概述 在我们常用的应用中&#xff0c;经常会有视图内容切换的场景&#xff0c;来展示更加丰富的内容。比如下面这个页面&#xff0c;点击底部的页签的选项&#xff0c;可以实现“首页…

研表究明,文字的序顺并不定一能响影GPT-4读阅

深度学习自然语言处理 原创作者&#xff1a;yy 很多年前&#xff0c;你一定在互联网上看过这张图&#xff0c;展示了人脑能够阅读和理解打乱顺序的单词和句子&#xff01;而最近东京大学的研究发现&#xff0c;大语言模型&#xff08;LLMs&#xff09; 尤其是 GPT-4&#xff0c…

MyBatisX插件

MyBatisX插件 MyBatis-Plus为我们提供了强大的mapper和service模板&#xff0c;能够大大的提高开发效率。 但是在真正开发过程中&#xff0c;MyBatis-Plus并不能为我们解决所有问题&#xff0c;例如一些复杂的SQL&#xff0c;多表联查&#xff0c;我们就需要自己去编写代码和SQ…

Redis基础系列-主从复制

Redis基础系列-主从复制 文章目录 Redis基础系列-主从复制1. 什么是 Redis 主从复制&#xff1f;2. 主从复制有什么好处&#xff1f;3. 如何配置 Redis 主从复制&#xff1f;4. 主从复制的验证4.1 如何查看主从搭建成功4.2 主从常见疑问4.3 主从常见命令 5. 主从复制的原理和工…

Go json 差异比较 json-diff(RFC6902)

Go json 差异比较 json-diff(RFC 6902) 毕业设计中过程中为了比较矢量图的差异而依据 RFC 6902 编写的一个包&#xff0c;现已开源&#xff1a; Json-diff 使用 go get -u github.com/520MianXiangDuiXiang520/json-diff序列化与反序列化 与官方 json 包的序列化和反序列化不…

NoSuchColumnFamilyException: org.apache.hadoop.hbase.regionserv

问题 在IDEA运行HBASE脚本时出现如下报错&#xff1a; org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException: org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException: Column family table does not exist in region hbase:meta,,1.1588230740 i…

深入学习锁--Lock各种使用方法

一、什么是Lock Lock是一个接口,通常所说的可重入锁是指Lock的一个实现子类ReentrantLock 二、Lock实现步骤&#xff1a; ①创建锁对象Lock lock new ReentrantLock(); ②加锁lock.lock(); ③释放锁lock.unlock(); import java.util.concurrent.locks.Lock; import java.util…

我有才打造专属个人或企业知识付费平台,核心功能设计

在当今信息爆炸的时代&#xff0c;知识管理已经成为了每个人必须面对的问题。然而&#xff0c;市面上的知识付费平台大多数都是通用的&#xff0c;无法满足个性化需求。 因此&#xff0c;我有才提供了一款专属定制的适合个人的知识付费平台。核心产品能力如下&#xff1a; 一…