5.最长回文子串
从定义分析回文子串特征
“回文”可以理解为从中间向两侧对称地扩散,例如 “abcdcba” 。
这个“中间”,可以是单个字符,也可以是两个相临的、且相等的字符,例如,以单个字符为中心: “abcdcba” ,这个就是以单个字符“d”为中心;以两个相邻相等的字符为中心: “abcddcba” ,这个就是以“dd”为中心。
- 如果字符串为空,那结果肯定为空字符串;
- 如果字符串长度为 1,那结果肯定为该字符串本身;
定义一个变量,设最长回文子串为:
let res = '';
遍历字符串 s
遍历字符串时,根据当前遍历到的字符s[i],分为两种情况:
- 中心为单个字符,即以该字符
s[i]为中心,判断从该字符能从两侧扩散出多长的回文子串; - 中心为两个相等字符,即当且仅当
s[i] === s[i + 1]时,当前可以作为回文子串的中心;
中心为单个字符
根据遍历到的i,设置三个变量:
// 左侧字符位置
let currLeft = i - 1;
// 右侧字符位置
let currRight = i + 1;
// 当前的回文子串
let currStr = '';
有了左右各自的字符位置,定义一个while循环,在左侧位置和右侧位置均有效的时候进入循环。
在循环中,判断以s[i]为中心,两侧的字符是否相等,如果相等,说明得到了一个回文子串,判断该回文子串和之前的回文子串的长度,如果更长,就缓存下来,PS:一旦遇到两侧字符不相等的情况,结束当前的 while 循环:
while (currLeft >= 0 && currRight < s.length) {
// 在两侧范围内判断回文
if (s[currLeft] === s[currRight]) {
// 是回文,将当前回文保存下来
currStr = s.slice(currLeft, currRight + 1);
currLeft--;
currRight++;
} else {
break;
}
}
if (currStr.length > res.length) {
// 如果当前的回文子串更长,那就缓存下来
res = currStr;
}
中心为两个相等的字符
注意:此时,一旦有两个相邻且相等的字符,那这两个字符就是回文子串,需要判断之前得到的回文子串的长度,如果之前的长度就小于 2,那么此时这两个相邻且相等的回文子串本就该缓存下来:
if (s[i] === s[i + 1]) {
if (res.length < 2) {
res = s.slice(i, i + 2);
}
}
同上,根据遍历的i,设置三个变量:
let _currLeft = i - 1;
// 中心长度为2,所以右侧的下标为当前遍历值i加上2!
let _currRight = i + 2;
let _currStr = '';
// 进入while循环的逻辑相同,即下标在字符串s中有效
while (_currLeft >= 0 && _currRight < s.length) {
if (s[_currLeft] === s[_currRight]) {
_currStr = s.slice(_currLeft, _currRight + 1);
_currLeft--;
_currRight++;
} else {
break;
}
}
if (_currStr.length > res.length) {
// 保存更长的子串
res = _currStr;
}
最终代码
function longestPalindrome(s: string): string {
if (s.length === 0) return '';
if (s.length === 1) return s;
// 用一个指针来指向回文子串的中心
// 既然是回文,那就会向向当前中心的两侧均匀扩散
let res = s[0];
// 先判断中心长度为1的情况
for (let i = 0; i < s.length; i++) {
/** -------------------分割线:以下代码用于判断回文子串的中心为单个字符的情况------------- */
// 先判断中心的长度为1
let currLeft = i - 1;
let currRight = i + 1;
let currStr = '';
while (currLeft >= 0 && currRight < s.length) {
// 在两侧范围内判断回文
if (s[currLeft] === s[currRight]) {
// 是回文,将当前回文保存下来
currStr = s.slice(currLeft, currRight + 1);
currLeft--;
currRight++;
} else {
break;
}
}
if (currStr.length > res.length) {
res = currStr;
}
/** -------------------分割线:以上代码用于判断回文子串的中心为单个字符的情况----------------- */
/** -------------------分割线:以下代码用于判断回文子串的中心为两个字符的情况----------------- */
//"abcdefghhgfedcba"\n
if (s[i] === s[i + 1]) {
if (res.length < 2) {
// 遇到俩相邻的字符是相等的,并且之前的结果的长度小于2,那么这俩相邻的可以视为回文子串
res = s.slice(i, i + 2);
}
let _currLeft = i - 1;
let _currRight = i + 2;
let _currStr = '';
while (_currLeft >= 0 && _currRight < s.length) {
if (s[_currLeft] === s[_currRight]) {
_currStr = s.slice(_currLeft, _currRight + 1);
_currLeft--;
_currRight++;
} else {
break;
}
}
if (_currStr.length > res.length) {
res = _currStr;
}
}
/** -------------------分割线:以上代码用于判断回文子串的中心为两个字符的情况---------------- */
}
return res;
}