LeetCode题解——Longest Palindromic Substring
题目:
给定一个字符串S,返回S中最长的回文子串。S最长为1000,且最长回文子串是唯一。
解法:
①遍历,对于每个字符,计算以它为中心的回文子串长度(长度为奇数),同时计算以它和右边相邻字符为中心的回文子串长度(长度为偶数)。时间为O(N2)。
②另外,有一个很奇妙的算法,称为Manacher算法,参考 http://www.cnblogs.com/daoluanxiaozi/p/longest-palindromic-substring.html ,时间为O(N)。
代码:
①直接扩展:
1 class Solution {
2 public:
3 string longestPalindrome(string s) {
4 int start = 0, max_len = 0;
5 for(int i = 0; i < s.size(); ++i)
6 {
7 int left, right, len;
8
9 len = 1; //以当前字符为中心的回文串
10 for(left = i - 1, right = i + 1; left >= 0 && right < s.size() && s[left] == s[right]; --left, ++right)
11 len += 2;
12 if(len > max_len)
13 {
14 start = left + 1;
15 max_len = len;
16 }
17
18 len = 0; //以当前字符以及右边相邻字符为中心的回文串
19 for(left = i, right = i + 1; left >= 0 && right < s.size() && s[left] == s[right]; --left, ++right)
20 len += 2;
21 if(len > max_len)
22 {
23 start = left + 1;
24 max_len = len;
25 }
26 }
27 return s.substr(start, max_len);
28 }
29 };
②Manacher算法:
1 class Solution {
2 public:
3 string longestPalindrome(string s) {
4 int slen = s.size();
5 if(slen == 0 || slen == 1)
6 return s;
7
8 const int nslen = 2 * slen + 3; //每个字符两边填充#,将奇偶长度的回文串统一处理
9 string ns(nslen, '#');
10 ns[0] = '^'; //开始和结尾添加特殊字符,防止越界
11 ns[nslen - 1] = '$';
12 for(int i = 0; i < slen; ++i)
13 ns[2 * i + 2] = s[i];
14
15 int id = 0, mx = 0; //id为已找到的最右回文串的中心下标,mx为该回文串的最右下标
16 int p[nslen]; //p保存以每个字符为中心的回文串的半径,这个半径值就是去掉填充字符之后实际回文串长度
17
18 for(int i = 1; i < nslen - 1; ++i)
19 {
20 p[i] = mx > i ? min(mx - i, p[2 * id - i]) : 0; //利用已有信息,给当前字符为中心的回文串赋半径初值
21
22 while(ns[i - 1 - p[i]] == ns[i + 1 + p[i]]) //向两边扩张
23 ++p[i];
24
25 if(i + p[i] > mx)
26 {
27 mx = i + p[i];
28 id = i;
29 }
30 }
31
32 int mid = 0, max_len = 0;
33 for(int i = 1; i < nslen - 1; ++i) //找出最长的回文串
34 if(p[i] > max_len)
35 {
36 mid = i;
37 max_len = p[i];
38 }
39
40 return s.substr((mid - 1 - max_len)/2, max_len); //求出回文串第一个字符的下标,返回回文串
41 }
42 };