解法一 Brute Force
遍历每个位置,并查看是否存在以这个位置为中心的(奇和偶)回文。
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
int mx=1, start=0;
for(int i=0;i<s.size()-1;i++){
updateMx(s, i, i, start, mx); // odd
updateMx(s, i, i+1, start, mx); // even
}
return s.substr(start, mx);
}
void updateMx(string s, int l, int r, int& start, int& mx){
while(l>=0 && r<s.size() && s[l]==s[r]){
l--; r++;
}
if(mx<r-l-1){ // at s[l]!=s[r]
mx = r-l-1;
start=l+1;
}
}
};
class Solution {
private int low, maxLen;
public String longestPalindrome(String s) {
int len = s.length();
if(len<2) return s;
for(int i=1;i<len;i++){
helper(s, i, i);
helper(s, i-1, i);
}
return s.substring(low, low+maxLen);
}
public void helper(String s, int l, int r){
while(l>=0 && r<s.length() && s.charAt(l)==s.charAt(r)){
l--;
r++;
}
if(maxLen < r-l-1){
maxLen = r-l-1;
low = l+1;
}
}
}
变形 no helper function
怎么样generalize奇偶的情况?
奇偶的区别其实就在中间位置,奇:中间位只出现一次;偶:中间位出现了两次
那么我们从中间(一位或两位)的两边开始check equality不就行了嘛?
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return s;
int mx=1, start=0;
for(int i=0;i<s.size();){
int l=i, r=i;
while(r<s.size()-1 && s[r]==s[r+1]) r++;
i=r+1;
while(l>0 && r<s.size()-1 && s[l-1]==s[r+1]){
l--; r++;
}
if(mx<r-l+1){ // at s[l]==s[r]
mx = r-l+1;
start = l;
}
}
return s.substr(start, mx);
}
};
解法二 DP
上面?这种方法是以每个位置为中心出发思考的,那么我们换一个思路
问题问:最长的palindrome -> 那么我们能不能找到所有的palindrome先 -> 我们能不能找到所有的string先
再回到问题,有了所有的strings怎么确定是不是palindrome? [str(i, j) means string from position i to j]
确定str(i, j)是不是palindrome,我们需要compare
s[i] == s[j]
s[i+1] == s[j-1]
s[i+2] == s[j-2]
......
确定str(i+1, j-1)是不是palindrome,我们需要compare
s[i+1] == s[j-1]
s[i+2] == s[j-2]
s[i+3] == s[j-3]
......
聪明的小伙伴一定嗅到了DP的味道:str(i, j) = ( s[i] == s[j] && str(i+1, j-1) )
如何遍历呢?
从之前得到的等式中,我们发现:
在处理str(i, j)的时候,
j-1已经ready了 -> j 在最外层从小至大遍历
i的遍历顺序无所谓,因为是基于j-1的i
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return "";
int mx=1, start=0, n=s.size();
bool dp[n][n]={false};
for(int j=0;j<n;j++){
dp[j][j] = true;
for(int i=0;i<j;i++){
if(s[i]==s[j] && (i==j-1 || dp[i+1][j-1])){
dp[i][j] = true;
if(mx<j-i+1){
mx = j-i+1;
start = i;
}
}
}
}
return s.substr(start, mx);
}
};
解法三 Manacher's Algorithm
[跟我一样害怕这个algorithm的小伙伴快来握个手,看了好几遍都是自以为懂了~]
另开一篇专门讲这个吧~~~ Manacher‘s Algorithm 马拉车算法
class Solution {
public:
string longestPalindrome(string s) {
if(s.empty()) return "";
string t = "$#";
for(char c: s){
t += c;
t += '#';
}
vector<int> lens(t.size(), 0);
int mx=0, id=0, resLen=0, resCenter=0;
for(int i=1;i<t.size();i++){
lens[i] = mx > i ? min(lens[2*id-i], mx-i):1;
while(t[i+lens[i]]==t[i-lens[i]]) lens[i]++;
if(mx<i+lens[i]){
mx = i+lens[i];
id = i;
}
if(resLen<lens[i]){
resLen = lens[i];
resCenter = i;
}
}
return s.substr((resCenter - resLen) / 2, resLen - 1);
}
};
What I can get from this question
1.Longest String can be represented as [1. the start point; 2. the length]
2.if (s[l] == s[r]) end up with s[l] != s[r]
if (s[l-1] == s[r+1]) end up with s[l] == s[r]
3. Error: runtime error: variable length array bound evaluates to non-positive value 0
bool dp[n][n] = {false} <- n cannot be zero