问题:https://leetcode.com/problems/longest-palindromic-substring/
给定一个字符串 S,求出 S 的最长回文子串
思路:
1. 回文:一个字符串从前和从后读一致。S = "ABBA" 从前读:ABBA,从后读:ABBA
2. 最简单的做法:列出所有的子串,并判断是否是回文,从中找出最长的。
表格列出了所有的子串,第 i 行是以 S 的第 i 个字符开始的子串。S = "ABBA"
1 | A | AB | ABB | ABBA |
2 | B | BB | BBA | |
3 | B | BA | ||
4 | A |
从最后一行往上,可以看到他们的共同部分,用红色标出。共同的子问题~动态规划。
3. 动态规划,自底向上求解问题
1) 对子问题的结果进行存储和表示
len[i]:包含S[i] 的最长回文子串的长度
all[i]:S 的后半部分S[i, ..., n - 1] 中的最长回文子串长度
2) 把子问题的结果合并成它的上一层
子问题比它的上一层增加了一个字符。已经求得 S[i+1, ...] 子问题的解:从S[i+1]开始有len[i+1]长的回文子串 S[i+1]~S[n], all[i+1]
** 如果S[i]和这个子串的后一个字符一样,则从S[i]开始的回文子串可以包含它
比如:S="ABBA",已知 len[1] = 2,S[0]=S[3]="A",所以len[0] = len[1]+2
** 反之,从S[i]开始的回文子串不可以包含它,则需要对该子串从两头开始遍历
用两个变量分别从头 S[i] 和从尾S[n+1]开始比较,当发生不等的时候,就重新从头开始。
源码
class Solution {
public:
string longestPalindrome(string s) {
int ssize = s.length();
int i, j, k, start;
int* len = new int[ssize];
int* all = new int[ssize];
//初始化,最长为1
len[ssize - ] = all[ssize - ] = ;
start = ssize - ; for(i = ssize - ; i >= ; --i)
{
//计算len[i]
if(i + len[i + ] + < ssize && s[i] == s[i + len[i + ] + ])//从S[i] 开始的回文子串包含S[i+1]开始的子串
len[i] = len[i + ] + ;
else//反之需要遍历
{
//j, k 表示从S[i] 开始可能的最长回文子串的区间
j = i;
k = i + len[i + ];
while(j < k)
{
if(s[j] == s[k])
{
++j;
--k;
}
else//两个字符不同时,j 需从头重新开始
{
j = i;
--k;
}
}
if(i == k && k == j)//
len[i] = ;
else
{
len[i] = (k - i + ) * ;//子串长度为偶数
if(j == k)//子串长度为奇数
len[i]--;
}
}
//计算all[i]
if(len[i] > all[i + ])
{
all[i] = len[i];
start = i;
}
else
{
if(len[i] == all[i + ])
start = i;
all[i] = all[i + ];
}
}
delete[] len;
delete[] all;
return s.substr(start, all[]);
}
};