题目
给你一个字符串 s,找到 s 中最长的回文子串。
思路
- s不为空并且长度小于2,直接返回
- 暴力破解-没思路的时候就先暴力破解,再想办法优化
通过双指针可以获取到所有的子串,然后拿到一个判断一个是否是满足回文条件。 - 判断回文
- 子串长度为偶数,left=len/2 - 1;right = len/2
- 子串长度为奇数,left=right = len/2
- 在子串长度范围内while(),判断s.charAt(left++)==s.charAt(right++)
- 如果出现false,返回false。
- 循环结束没有出现false,将s.len通过Math.max()存入待返回结果
代码尝试-有错
//最长回文子串
@Test
void longestPalindrome(){
String s = "babad";
int max = 0;
String result = null;
//获取所有子串
for (int i = 0; i < s.length(); i++) {
for (int j = i; j < s.length(); j++) {
String subStr = s.substring(i, j);
boolean isP = isPalindrome(subStr);
if (isP) {
if (subStr.length() > max) {
result = subStr;
max = subStr.length();
}
}
}
}
System.out.println(result);
}
boolean isPalindrome(String s) {
int left, right;
int len = s.length();
if (len % 2 == 0) {
left = len / 2 - 1;
right = len / 2;
}else{
left = right = len / 2;
}
while (left >= 0) {
if (s.charAt(left) == s.charAt(right)) {
left--;
right++;
} else return false;
}
return true;
}
大致逻辑正常,但是返回有问题,关于subString()方法的问题
subString知识点
s.subString(int beginIndex,int endIndex)源码:
字符串生成的时候如果beginIndex和endIndex在同一个位置,不会生成beginIndex此位置字符的字符串,而是生成长度为0的"“。
所以在生成子串的内层for循环条件里,j不能从0开始,否则生成的第一个子串为”"。
要生成外层for循环里i位置的子字符串,那么j就一定要比i靠后一位。
jdk api 1.8:
解决
for (int j = i+1; j <= s.length(); j++) {
暴力破解几乎必定超时
第一大拦路虎——动态规划
遍历子字符串有两种方式:一种是固定边界,一种是固定子串长度。
我看leetcode官方解答用的是固定子串长度进行遍历
第一个for循环将子串长度固定,然后第二个for循环通过移动左边界进行移动子串的位置,就像一个固定大小的活动窗口在字符串上移动,圈出来一个一个的子字符串。
举例展示为什么动态规划可以节省时间?
以固定子串长度为例:
(当固定子字符串长度分别为len时,判断是否是回文需要判断的字符位置组合)
len2:0-1; 1-2; 2-3; 3-4; 4-5……
len3:0-2; 1-3; 2-4; 3-5; 4-6……
len4:0-3,1-2;1-4,2-3; 3-6,4-5; 4-7,5-6; ……
通过这三行举例,当子字符串长度为4时,比较过程中有一半都是在之前len=2时判断过的。
所以动态规划的核心就是开辟出一个空间(数组、list、map…),把之前判断过的结果存进去,后面可以拿到直接用,可以节省大量时间。
也就是用空间换时间。
那么固定边界也可以使用空间换时间吗?
上面的暴力破解也属于固定边界的一种,固定左边界,移动右边界来遍历子字符串。尝试修改一下上面的暴力破解代码,核心思想就是new一个数组出来,把每次判断出来的比较短的回文字符串存进去,后面判断的时候先查数组,如果能查到就不需要再判断一次了。
{
if(s.length() < 2){
return s;
}
int max = 0;
String result = null;
//空间换时间的容器
boolean[][] ap = new boolean[s.length()][s.length()];
//所有一个字符的子串默认都满足回文判断
for (int i = 0; i < s.length(); i++) {
ap[i][i] = true;
}
int start = 0;
int end = 0;
//获取所有子串
for (int i = 0; i < s.length(); i++) {
for (int j = i+1; j < s.length(); j++) {
boolean flag = true;
if (!ap[i][j]) {
int m = i;
int n = j;
while (m < n) {
if (s.charAt(m) == s.charAt(n)) {
m++;
n--;
} else {
flag = false;
break;
}
}
ap[i][j] = flag;
if (flag) {
if (j - i + 1 > max) {
max = j - i + 1;
start = i;
end = j;
}
}
}
}
}
result = s.substring(start, end + 1);
return result;
}
对应一个初学者来说,算法真的是太不友好了😭😭😭
写的时候就能感觉到建了太多变量了……不知道是不是暴力破解逻辑的硬伤
去看标准答案去😭😭
不过也不要太气馁,初学阶段感觉算法拼的还是知识储备,就像能看懂答案但是不会自己做的数学题一样
继续加油
希望我永远都拥有开始的勇气。