题目来源:
http://acm.hdu.edu.cn/showproblem.php?pid=3068
https://leetcode.com/problems/longest-palindromic-substring/
问题描述:
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 回文就是正反读都是一样的字符串,如aba, abba等
分析:
若直接使用对某个位置向两边搜索的方法leetcode能通过;
而hdoj中“字符串长度len <= 110000”,会超时!看来只能用Manacher算法。
这个算法可以看:http://blog.youkuaiyun.com/yzl_rex/article/details/7908259 (其中输出结果那边的代码是错的)
关键点为下图,即求i处时可以利用关于id对称的j处的值。
leetcode上关于此题给出了几种解法:https://leetcode.com/articles/longest-palindromic-substring/
Approach #1 (Longest Common Substring) [Accepted]
Approach #2 (Brute Force) [Time Limit Exceeded]
Approach #3 (Dynamic Programming) [Accepted]
Approach #4 (Expand Around Center) [Accepted]
Approach #5 (Manacher's Algorithm) [Accepted]
尤其要注意Approach #1,求和反串的LCS:
Let’s try another example: S=''abacdfgdcaba'', S′=''abacdgfdcaba''.
The longest common substring between S and S is ''abacd''. Clearly, this is not a valid palindrome.
代码:
对某个位置向两边搜索的方法(leetcode上通过)
class Solution {
public:
string longestPalindrome(string s) {
char ns[102400];
int ans[102400];
int i,j,k;
int slen=s.length();
//方便偶数长时处理
for(i=0;i<slen;i++)
ns[2*i]='#',ns[2*i+1]=s[i];
ns[2*i]='#';
for(i=0;i<=2*slen;i++){
ans[i]=0;
for(j=1;i-j>=0 && i+j<=2*slen;j++){ //ns开头若再补上个‘*’,配合结尾的‘\0’,则不用判断i,j越界
if(ns[i-j]==ns[i+j])
ans[i]++;
else
break;
}
}
int idx=0;
for(i=0;i<=2*slen;i++)
if(ans[idx]<ans[i]){
idx=i;
}
int l=ans[idx];
string as="";
for(i=idx-l+1;i<=idx+l-1;i+=2)
as+=ns[i];
return as;
}
};
Manacher算法(HDOJ通过)时间复杂度O(n)
#include <iostream>
using namespace std;
#include <string>
const int SZ=110000;
char ns[2*SZ+5];
int ans[2*SZ+5];
int main()
{
int i;
string s;
while(cin>>s){
int slen=s.length();
for(i=0;i<slen;i++)
ns[2*i+1]='#',ns[2*i+2]=s[i];
ns[0]='*';
ns[2*i+1]='#';
int mans=0,id,mx=0;
for(i=0;i<=2*slen+1;i++){
if(mx>i)
ans[i]=min(mx-i,ans[2*id-i]);
else
ans[i]=1;
while(ns[i-ans[i]]==ns[i+ans[i]])
ans[i]++; //因为#的存在所以只加1
if(i+ans[i]>mx){
mx=i+ans[i];
id=i;
}
if(mans<ans[i])
mans=ans[i];
}
cout<<mans-1<<endl;
}
return 0;
}