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.mzph.cn/news/213094.shtml

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

相关文章

设计CPU功能的数字电路

实验目的(1)熟悉Multisim 电路仿真软件的操作界面和功能; (2)掌握逻辑电路综合设计,并采用仿真软件进行仿真。 实验内容1.试设计一个简易CPU功能的数字电路,实验至少要求采用4个74HC/HCT194作为4个存储单元(可以预先对存储单元存储数据),74HC283作为计算单元。请实现…

多维时序 | MATLAB实现RIME-CNN-LSTM-Multihead-Attention多头注意力机制多变量时间序列预测

多维时序 | MATLAB实现RIME-CNN-LSTM-Multihead-Attention多头注意力机制多变量时间序列预测 目录 多维时序 | MATLAB实现RIME-CNN-LSTM-Multihead-Attention多头注意力机制多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现RIME-CNN-…

idea本地调试hadoop 遇到的几个问题

1.DEA对MapReduce的toString调用报错&#xff1a;Method threw ‘java.lang.IllegalStateException‘ exception. Cannot evaluate org.apache.hadoop.mapreduc 解决方法&#xff1a;关闭 IDEA 中的启用“ tostring() ”对象视图 2.代码和hdfs路径都对的情况下&#xff0c;程序…

架构设计系列之基础:初探软件架构设计

11 月开始突发奇想&#xff0c;想把自己在公司内部做的技术培训、平时的技术总结等等的内容分享出来&#xff0c;于是就开通了一个 Wechat 订阅号&#xff08;灸哥漫谈&#xff09;&#xff0c;开始同步发送内容。 今天&#xff08;12 月 10 日&#xff09;也同步在 CSDN 上开通…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《面向微电网群的云储能经济-低碳-可靠多目标优化配置方法》

这篇文章的标题涵盖了以下关键信息&#xff1a; 面向微电网群&#xff1a;研究的重点是微电网群&#xff0c;这可能指的是多个微电网系统的集合&#xff0c;而不仅仅是一个单独的微电网。微电网是指由分布式能源资源、储能系统和智能控制组成的小型电力系统&#xff0c;通常能够…

实现加盐加密方法以及java nio中基于MappedByteBuffer操作大文件

自己实现 传统MD5可通过彩虹表暴力破解&#xff0c; 加盐加密算法是一种常用的密码保护方法&#xff0c;它将一个随机字符串&#xff08;盐&#xff09;添加到原始密码中&#xff0c;然后再进行加密处理。 1. 每次调用方法产生一个唯一盐值&#xff08;UUID &#xff09;密码…

UDS诊断 10服务

文章目录 简介诊断会话切换请求和响应1、请求2、子功能3、肯定响应4、否定响应5、特殊的NRC 为什么划分不同会话报文示例UDS中常用 NRC参考 简介 10服务&#xff0c;即 Diagnostic Session Control&#xff08;诊断会话控制&#xff09;服务用于启用服务器中的不同诊断会话&am…

(四) python门面模式

文章目录 4.1 结构型设计模式4.1.1 简介4.1.2 常见的几种结构型设计模式 4.2 理解门面设计模式4.2.1 门面设计模式概述4.2.2 门面设计模式的作用 4.3 UML类图4.3.1 门面4.3.2 系统4.3.3 客户端 4.4 门面模式的代码实现4.4.1 场景&#xff1a;4.4.2 python实现 4.5 原理&#xf…

渲染(iOS渲染过程解析)

渲染 渲染原理 一个硬核硬件科普视频 CPU和GPU CPU&#xff08;Central Processing Unit&#xff09;&#xff1a;现代计算机整个系统的运算核心、控制核心&#xff0c;适合串行计算。GPU&#xff08;Graphics Processing Unit&#xff09;&#xff1a;可进行绘图运算工作的…

安防音频接口选型的高性能国产芯片分析

在人工智能兴起之后&#xff0c;安防市场就成为了其全球最大的市场&#xff0c;也是成功落地的最主要场景之一。对于安防应用而言&#xff0c;智慧摄像头、智慧交通、智慧城市等概念的不断涌现&#xff0c;对于芯片产业催生出海量需求。今天&#xff0c;我将为大家梳理GLOBALCH…

Linux——缓冲区与实现C库的fopen,fwrite,fclose

目录 一.缓冲区 1缓冲区的概念 2.缓冲区存在的意义 3.缓冲区刷新策略 4.什么是刷新&#xff1f; C语言的缓冲区在哪里&#xff1f; ​编辑 仿写C库里的fopen&#xff0c;fclose&#xff0c;fwrite。 mystdio.h mystdio.c main.c(向文件中写入20次msg) 一.缓冲区 1…

b站pwn的学习总结

写的很乱 1.c语言的运行过程 了解了c语言需要经过以上2个过程&#xff08;编译和汇编&#xff09;&#xff0c;才能让机器按指令运行。机器只能听得懂机器码&#xff0c;所以要“汇编”。 那问题就来了&#xff0c;“编译”这个动作有啥用&#xff0c;c语言这种高级语言&…

玩转大数据10:深度学习与神经网络在大数据中的应用

目录 1. 引言&#xff1a;深度学习和神经网络在大数据中的重要性和应用场景 2. 深度学习的基本概念和架构 3. Java中的深度学习框架 3.1. Deeplearning4j框架介绍及Java编程模型 3.2. DL4J、Keras和TensorFlow的集成 4. 大数据与深度学习的结合 4.1. 大数据与深度学…

电脑端同时登录多个微信

1、建立一个txt文件 2、右击微信查看应用的属性&#xff0c;记录文件的位置 3、将步骤二得到的路径按照下方的格式输入到步骤一的文本中 4、保存之后将文本后缀名的.txt改成.bat 5、在未登录微信的情况下&#xff0c;双击即可得到两个微信登录窗口

解决idea 通过build project 手动触发热部署失败

在debug运行项目的过程中&#xff0c;并且保证&#xff08;不添加方法&#xff0c;不修改方法名&#xff09;一定的规则的情况下&#xff0c;可以通过build project 来手动热部署项目&#xff0c;也就是会交换class文件与resouces文件。 设置项 Edit Configurations Modify Op…

计算机图形学理论(1):建模基础

本系列根据国外一个图形小哥的讲解为本&#xff0c;整合互联网的一些资料&#xff0c;结合自己的一些理解。 场景的组成部分 场景相当于一个或多个模型的集合。模型包含以下内容&#xff1a; 结构描述&#xff1a;几何形状&#xff0c;如顶点、纹理坐标等表面描述&#xff1a…

Vue3中的defineModel

目录 一、vue3的defineModel介绍 二、defineModel使用 &#xff08;1&#xff09;在vite.config.js中开启 &#xff08;2&#xff09;子组件 &#xff08;3&#xff09;父组件 一、vue3的defineModel介绍 为什么要使用到defineModel呢&#xff1f;这里有这样一种场景&…

“快速排序:一种美丽的算法混沌”(1.hoare)

欢迎来到我的博客&#xff01;在今天的文章中&#xff0c;我将采用一种独特且直观的方式来探讨我们的主题&#xff1a;我会使用一幅图像来贯穿整篇文章的讲解。这幅精心设计的图表不仅是我们讨论的核心&#xff0c;也是一个视觉辅助工具&#xff0c;帮助你更深入地理解和掌握本…

学习深度强化学习---第2部分----RL动态规划相关算法

文章目录 2.1节 动态规划简介2.2节 值函数与贝尔曼方程2.3节 策略评估2.4节 策略改进2.5节 最优值函数与最优策略2.6节 值迭代与策略迭代2.7节 动态规划求解最优策略 本部分视频所在地址&#xff1a;深度强化学习的理论与实践 2.1节 动态规划简介 态规划有两种思路&#xff1…

前端 Web Workers 简介

简介 以前我们总说&#xff0c;JS 是单线程没有多线程&#xff0c;当 JS 在页面中运行长耗时同步任务的时候就会导致页面假死影响用户体验&#xff0c;从而需要设置把任务放在任务队列中&#xff1b;执行任务队列中的任务也并非多线程进行的&#xff0c;然而现在 HTML5 提供了…