题目
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad” 输出:“bab” 解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd” 输出:“bb”
示例 3:
输入:s = “a” 输出:“a”
示例 4:
输入:s = “ac” 输出:“a”
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母(大写和/或小写)组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
有时间的时候会补充具体思路
一、暴力解法
class Solution {
public String longestPalindrome(String s) {
int max=1;//存储最大长度,用来判断是否需要改变左右指针
int left=0;//左指针
int right=0;//右指针
//下面为两类特殊情况
if(s.length()==1)return s;
else if(s.length()==2) {
if(s.charAt(0)==s.charAt(1))
return s;
else
return s.charAt(0)+"";
}
//一般情况(数组长度大于2时)
for (int i = 1; i < s.length()-1; i++) {
int val=1;
int m=i;int n=i;//m和n是每一次遍历的临时左右指针
char c=s.charAt(i);
//注意防止越界,下面是解决```aa```这类特殊情况
while(++m<s.length()&&s.charAt(m)==c) {
val++;
}
while(--n>=0&&s.charAt(n)==c) {
val++;
}
//退出时需要把m、n还原
m=m==i?m:--m;
n=n==i?n:++n;
int mark=0;
int mark1=0;//后续用mark和mark1是否相等来判断n是否需要+1
while(++mark>0&&++m<s.length()&&++mark1>0&&--n>=0&&s.charAt(n)==s.charAt(m)) {
val+=2;
}
//left和right指针是否发生变化
if(max<val) {
max=val;
right=m-1;
if(mark1==mark)
left=n+1;
else left=n;
}
}
//【left,right】字符串
return s.substring(left,right+1);
}
}
/* 1)s.length<1000,是提示我们o(n²)的时间复杂度是可以的
* 2) 需要注意的是:在赋值时,if后面一定要慎重考虑是否有else
* 3)在while(++a<5&&--b>0)时,如果 a、b都要在判断时进行变化,一定要注意,如果a不符合, a变b不变,如果b需要用,这时需要添加标志去判断 b是否变化如while(++mark>0&&++a<5&&++mark1>0&&--b>0)
* 4)这种求字符串中子字符串的问题,如果该子字符串本身在字符串中连续,最简单的方法当然是找 到left和right指针。
* 5)【···aa···】的解决策略
*/
二、动态规划
用boolean数组dq[i][j]表示字符串中i到j这个子字符串是回文,那么如果dq[i][j]为真,至少s[i]==s[j],其次i+1到j-1字符串也为回文,即dq[i+1][j-1]为真,特殊情况是当真有2个或者3个字符时我们不需要判断内部字符串是否为回文。我们发现,当寻找dq[i][j]时需要dq[i+1][j-1]的结果,也就是这时候j-1需要被我们在遍历j之前遍历,并且[i+1]在[i]之前遍历,决定了外层遍历不能是i,只能是j!(可以看下图帮助理解)代码实现如下:
class Solution {
/*
思考:为什么可以去掉判断数组长度为1的情况呢?
*因为此使ans[0]和ans[1]都为初始值0,截取下来的字符串为【0,1),所以不用分类讨论*/
public String longestPalindrome(String s) {
int n=s.length();
int[] ans=new int[2];
boolean[][] dq=new boolean[n][n];
int max=1;
for (int j = 0; j <n; j++) {
for (int i = 0; i <j; i++) {
dq[i][j]=(s.charAt(i)==s.charAt(j))&&(j-i<3||dq[i+1][j-1]);
if(dq[i][j]&&max<j-i+1) {
max=j-i+1;
ans[0]=i;
ans[1]=j;
}
}
}
return s.substring(ans[0],ans[1]+1);
}
}
注:很明显,动态规划的逻辑比第一种方法的逻辑要清晰很多,只是可惜在leetcode上运行要300多ms,前一种方法只用了44ms。