第4、5周的时候一直在赶DDL,写的题都没时间上来写题解…
懈怠久了后来连题都没写更不要说题解了…orz
少年啊少年啊,快鼓起劲来~
题目大意
寻找字符串中最长的回文子串。
随机 Pick 到这题时我的内心是拒绝的,最开始看到 Palindromic 的时候一脸懵逼,觉得题目中肯定会解释这个词意吧,结果…/微笑脸
百度了一番,才知道是“回文”:
“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。
个人题解
题意上没有太大的障碍,而且除了暴力也没有什么特别好的想法;不过,暴力也可以有一点小技巧。
暴力的思路很简单,用长度的奇偶对回文串进行分类,有如下两种形式:
考察它们的中心点,奇回文串有唯一的一个中心点,而偶回文串则有两个中心点。
那么对于字符串中的每一个位置,它可以是一个奇回文串的中心 a (至少它本身可以构成一个回文串),还可能是一个偶回文串的左中心 b 或右中心 c;也即是说,每个位置至少对应一个子回文串,至多对应三个子回文串。
取每个位置对应的子回文串中具有最大长度者作为该位置对应的主子回文串,那么最长子回文串必定在这些主子回文串中。
对于字符串的任意一个位置,得到它的主子回文串最多需要遍历字符串的所有位置,即是对于一个位置复杂度为 O(n) ;求出所有位置上的主子回文串的复杂度为 O(n2)。
最后考虑优化,使用 MAX
记录当前最大的长度,idx
表示当前位置下标。那么如果 (idx + 1) * 2 <= MAX
或者 (size - idx) * 2 <= MAX
,显然就没有继续求下去的必要了,因为该位置的左端或者右端已经限定它不可能获得更长的回文串。
除此之外,因为一个偶回文串存在两个中心点,那么对这两个中心点所在位置进行考察时,会得到相同的子回文串,也即是存在重复;为了避免重复的考察,从字符串的中心 (size / 2
) 出发,对于中心左边的位置只需要考虑作为奇中心或右中心点(作为左中心点的情况如果存在,那么前一个位置作为右中心点一定已经考察过该情况了);对于中心右边的位置只需要考虑作为奇中心或左中心点。
源代码
class Solution {
public:
string longestPalindrome(string s) {
int center = s.size() / 2;
int target_a = center;
int target_b = center;
int a = getLongestPalidrome(s, center, 0, true, target_a);
int b = getLongestPalidrome(s, center, 0, false, target_b);
if (a >= b) {
return s.substr(target_a - a / 2, a);
} else {
if (b % 2 == 0)
return s.substr(target_b - b / 2 + 1, b);
else
return s.substr(target_b - b / 2, b);
}
}
static int getLongestPalidrome(const string &s, int center, int MAX, bool direction, int &target) {
if (center < 0 || center >= s.size()) return MAX;
if (direction == true) { // LEFT == true
if ((center + 1) * 2 < MAX) return MAX; // when the length can no longer be larger than MAX
int cnt_exact = 1; // exactly in the center
for (int i = 1; center + i < s.size() && center - i >= 0; i++) {
if (s[center + i] == s[center - i]) cnt_exact += 2;
else break;
}
int cnt_right = 0; // right
if (center - 1 >= 0 && s[center - 1] == s[center]) {
cnt_right = 2;
for (int i = 1; center + i < s.size() && center - 1 - i >= 0; i++) {
if (s[center + i] == s[center - 1 - i]) cnt_right += 2;
else break;
}
}
if (cnt_exact >= MAX) {
MAX = cnt_exact;
target = center;
}
if (cnt_right >= MAX) {
MAX = cnt_right;
target = center;
}
return getLongestPalidrome(s, center - 1, MAX, direction, target);
} else {
if (2 * (s.size() - center) < MAX ) return MAX;
int cnt_exact = 1; // exactly in the center
for (int i = 1; center + i < s.size() && center - i >= 0; i++) {
if (s[center + i] == s[center - i]) cnt_exact += 2;
else break;
}
int cnt_left = 0; // left
if (center + 1 < s.size() && s[center + 1] == s[center]) {
cnt_left = 2;
for (int i = 1; center + 1 + i < s.size() && center - i >= 0; i++) {
if (s[center + 1 + i] == s[center - i]) cnt_left += 2;
else break;
}
}
if (cnt_exact >= MAX) {
MAX = cnt_exact;
target = center;
}
if (cnt_left >= MAX) {
MAX = cnt_left;
target = center;
}
return getLongestPalidrome(s, center + 1, MAX, direction, target);
}
}
};
吐槽
这题明明写着 `there exists one unique longest palindromic substring
,然而最后一个样例是 abcda
,每一个字母都是最长子回文串,然后就 WA 了,手动再见。