题目描述:传送门
思路与方法
对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后,它仍然是个回文串。例如对于字符串“ababa”,如果我们已经知道“bab”是回文串,那么“ababa”一定是回文串,这是因为它的首尾两个字母都是“a”。
用DP[i][j]表示字符串s的第i个位置到第j个位置组成的串是否为回文串,根据上面的思路,可知需要判断其子串是否为回文串,即DP[i+1][j-1]是否为回文串且s[i]是否等于s[j],因此状态转移方程为:
DP[i][j] = DP[i+1][j-1] && s[i] == s[j]
也就是说,只有s[i+1:j−1]是回文串,并且ss的第i和j个字母相同时,s[i:j] 才会是回文串。
上述情况是建立在子串长度大于2的前提之上的,还需要考虑边界条件,即子串长度为1或者2的情况。对于长度为 1 的子串,它显然是个回文串;对于长度为 2 的子串,只要它的两个字母相同,它就是一个回文串。因此我们就可以写出动态规划的边界条件:
长度为1时:DP[i][i] = true;
长度为2时:DP[i][i+1] = s[i] == s[i+1];
注意:在状态转移方程中,我们是从长度较短的字符串向长度较长的字符串进行转移的,因此一定要注意动态规划的循环顺序。
class Solution { public: string longestPalindrome(string s) { if(s.length() < 2) return s; vector<vector<bool>> dp(s.length(), vector<bool>(s.length(), false)); string res; int pos, maxLen = 0; for(int l = 0; l < s.length(); l++) //以长度为外层循环 { for (int i = 0; i + l < s.length(); i++) //依次选择每个字符作为起始字符 { int j = i + l; //以当前字符位置+长度作为终止字符位置 if(l == 0) dp[i][j] = 1; //长度为0时,代表单个字符,肯定是回文串 else if(l == 1) dp[i][j] = s[i] == s[j]; //长度为1时,如果两个字符相同,则为回文串 else dp[i][j] = (s[i] == s[j] && dp[i+1][j-1]); //其他情况进行状态转移 if(dp[i][j] && l + 1 > maxLen) //如果当前是字符串,且长度最大,则保存新的位置和长度 { maxLen = l + 1; pos = i; } } } res = s.substr(pos, maxLen); return res; } };