一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
前言
笔者除了大学时期选修过《算法设计与分析》和《数据结构》还是浑浑噩噩度过的(当时觉得和编程没多大关系),其他时间对算法接触也比较少,但是随着开发时间变长对一些底层代码/处理机制有所接触越发觉得算法的重要性,所以决定开始系统的学习(主要是刷力扣上的题目)和整理,也希望还没开始学习的人尽早开始。
系列文章收录《算法》专栏中。
问题描述
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
提示:
- 1 <= s.length <= 1000
- s 仅由数字和英文字母组成
剖析
暴力破解
首先解释下回文,回文就是对称的字符串,即正过来返回来都相等。看到这道题想了下,最简单的就是暴力破解,两层for循环从前到后扩大截取子字符串进行判断是否对称,在这个过程中记录当前最长的子字符串和最长的长度,如果后面没有更长的就返回当前的。
至于判断字符串是否对称:设字符串长度为l,i为字符串的字符下标,那么只需要判断i下标下的字符是否和l-i-1下标的字符是否相等即可。
代码如下:
public static boolean isPalindromic(String s) {
for (int i = 0; i < s.length() / 2; i++) {
if (s.charAt(i) != s.charAt(s.length() - i - 1)) {
return false;
}
}
return true;
}
// 暴力解法
public static String longestPalindrome(String s) {
//最终返回的字符串
String result = "";
int max = 0;
for (int i = 0; i < s.length(); i++) {
for (int j = i + 1; j <= s.length(); j++) {
String subS = s.substring(i, j);
if (isPalindromic(subS) && subS.length() > max) {
max = Math.max(max, subS.length());
result = subS;
}
}
}
return result;
}
动态规划
动态规划的核心思想就是:从内到外归纳总结再归纳总结,后项再前项的基础上进行。对于数学来说就是写转移方程了,最内层的项是一个特殊情况。
所以针对于这道题,最内层的项就是当有一个字符的时候为回文,大于等于2就是需要最外层的左右两边到最内层的左右两边相等。
我们用 P(i,j)表示字符串 s 的第 i 到 j 个字母组成的串是否为回文,如果为回文就是true,那么它的转移方程就是:P(i,j)=P(i+1,j−1)∧(Si==Sj),Si、Sj就是最外层左右两边的字符,特殊情况就是P(i,i)=true。
代码如下:
代码
public static String longestPalindrome(String s) {
if (s.length() < 2) {
return s;
}
//最长回文字串的左边下标
int begin = 0;
//当前最长回文字串长度
int max = 1;
boolean[][] dp = new boolean[s.length()][s.length()];
//特殊情况赋值
for (int i = 0; i < s.length(); i++) {
dp[i][i] = true;
}
//特殊情况已经处理,所以从2开始
for (int l = 2; l <= s.length(); l++) {
for (int i = 0; i < s.length(); i++) {
int j = l + i - 1;
//到达了右边界
if (j >= s.length()) {
break;
}
if (s.charAt(i) != s.charAt(j)) {
dp[i][j] = false;
} else {
//已经经过了if判断所以i下标和j下标的字符必定相等,当长度为2时dp[i][j]必定为true。
if (l == 2) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
//是回文的话就判断长度是否大于,大于就进行替换
if (dp[i][j] && (j - i + 1) > max) {
begin = i;
max = (j - i + 1);
}
}
}
return s.substring(begin, begin + max);
}