题目大意:给定一个字符串,找出其最长的子回文串将其返回。所谓回文串,即倒序排列与正序排列一致。
说明:假定字符串长度不超过1000
示例:输入"babad",输出"bab"或者"aba"
解题思路:关于题目有几点要注意的。一是题目默认是有解的,当输入如"abcde"的形式时,解当为"a"或者任一字符代表的字符串;二是有多个解,只需输出一个即可。
在思考这道题时,最先冒出的想法很简单,即以第一个字符为头开始,逐步增加字符串长度,判断是否为回文串。若不是,记录最大长,并跳到下一字符,以其开头。这样做虽然简单,但时间复杂度过高,很容易出现超时。
联想到之前做的一道题,我试着用“滑动窗”法去解决它。如下:
class Solution {
public:
string longestPalindrome(string s) {
int j,k1,k2,left=0,right=0,res=0; //记录滑动窗的左右边界
string ans;
k1=1;
k2=1;
for(j=2;j<s.length();j++) //遍历字符串
{
if(j>=2*k1&&s.at(j)==s.at(j-2*k1)) //如果奇对称的话
{
if(2*k1+1>res)
{
left=j-2*k1; //更新左右的值
right=j;
res=2*k1+1;
}
k1++;
}
else
k1=1;
if(j>=2*k2-1&&s.at(j)==s.at(j+1-2*k2)) //如果偶对称的话
{
if(2*k2>res)
{
left=j+1-2*k2;
right=j;
res=2*k2;
}
k2++;
}
else
k2=1;
}
ans=s.substr(left,res);
return ans;
}
};
思路也不复杂。从左往右逐个遍历字符串中的元素,判断该元素与之前的元素是否形成对称。若形成对称,则保留对称信息,继续遍历下一元素。此方法在本地测试时没有出现问题,但上传后结果是WA。我思考了一下,发现在编写程序时,漏掉了一种些情况。比如输入"aaaaa",结果输出"aaaa",原因在于遍历到第3个‘a’时,程序只检查了其与之前元素对称的情况,而跳过了第三个'a'中心对称的情形。另外,对于结果是单个字符的情况,也没有做处理。
在做了一定改动结果仍不正确后,我参考了solution中的答案。其中比较有代表性的就是利用动态规划的思想去解决。
在这种方法中,首先需要给出动态规划的递推式。本题中的递推式如下:
其中,
简单地说,就是用P(i,j)表示对于从下标i到j的子字符串是否为回文串,如果其是回文串,则为逻辑真,否则为逻辑假。那么对于一个回文串,如果其一前一后两个字符又相同,则可将其“扩充”成更长的回文串。基于此,只要找出给定字符串中长为1或者2的子回文串,然后对其逐个扩充至最长,记录下最长回文串的信息,返回即可。实现代码如下:
class Solution {
public:
string longestPalindrome(string s) {
int i,j,k,t=0,palindrome[2000][2],res=0,ans=1;
string ret;
memset(palindrome,0,sizeof(palindrome));
for(i=0;i<s.length()-1;i++) //最后一个字符单独考虑
{
if(s.at(i)==s.at(i+1))
{
palindrome[i][0]=i;
palindrome[i][1]=i+1;
palindrome[s.length()+t][0]=i;
palindrome[s.length()+t][1]=i;
t++;
}
else
{
palindrome[i][0]=i;
palindrome[i][1]=i;
}
}
palindrome[s.length()-1][0]=s.length()-1;
palindrome[s.length()-1][1]=s.length()-1;
for(j=0;j<s.length()+t;j++) //动态规划以扩充回文串
{
k=0;
while(palindrome[j][1]+k<=s.length()-1&&palindrome[j][0]>=k&&s.at(palindrome[j][0]-k)==s.at(palindrome[j][1]+k))
k++;
if(res<k*2+(palindrome[j][0]==palindrome[j][1]?-1:0))
{
res=k*2+(palindrome[j][0]==palindrome[j][1]?-1:0);
ans=j;
palindrome[j][0]=palindrome[j][0]-k+1;
palindrome[j][1]=palindrome[j][1]+k-1;
}
}
ret=s.substr(palindrome[ans][0],res);
return ret;
}
};
值得注意的是,在编写代码时比较容易遗漏一些情形。比如输入连续重复字符时,若没有变量t,就会出现比正确结果少1的情况。
本方法的时间复杂度时O(n^2),在接受范围内。在solution中还提供了一种复杂度仅为O(n)的方法,不过由于不具有一般性,此处不赘述。
动态规划属于比较常见的处理手段,其难点在于想到以动态规划的方法去解题以及建立动态规划的关系式。希望能从这道题中引以为戒,学会更多数据处理方法。