1. 题目:
Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.
注:
palindromic substring :回文序列,如:aba,abba 等。
2.1 C++ 暴力解决—— 时间复杂度O(N³)
思路:
(1). 构造一个map,存储原字符出现的所有位置;
(2). 从头到位扫描字符串,根据map中的位置,选取子字符串,判断是否为回文序列
class Solution {
public:
string longestPalindrome(string s) {
unsigned long long string_len=s.length();
if(string_len==0)
return "";
if(string_len==1)
return s;
string current_str="",longest_str="";
unsigned long long current_len=0,longest_len=0;
map<char,vector<unsigned long long> >char_pos_map;
for(int i=0;i<string_len;i++){
map<char,vector<unsigned long long> >::iterator char_pos_map_it=char_pos_map.find(s[i]);
if(char_pos_map_it==char_pos_map.end()) {
vector<unsigned long long> pos_list;
pos_list.push_back(i);
char_pos_map.insert(pair<char, vector<unsigned long long > >((char)s[i],pos_list));
} else {
vector<unsigned long long> & pos_list=char_pos_map_it->second;
pos_list.push_back(i);
}
} //map存储每个字符出现的位置
for(int index_head = 0;index_head<string_len;index_head++) {
std::map<char, vector<unsigned long long > >::iterator it = char_pos_map.find(s[index_head]);
if( it->second.size()==1) {
current_len = 1;
current_str = s[index_head];
if(current_len>longest_len) {
longest_str = current_str;
longest_len = current_len; //只出现一次的字符
}
} else {
vector<unsigned long long> & tmp_vec = it->second;
unsigned long long index_num = tmp_vec.size();
unsigned long long tmp_index_head = index_head;
for(long long j=(long long)(index_num-1);j>=0;j--) {
tmp_index_head = index_head;
unsigned long long tmp_index_tail = tmp_vec[j];
if(tmp_index_tail<tmp_index_head)
continue;
current_len = tmp_index_tail-tmp_index_head+1;
if( current_len==0 || current_len < longest_len)
continue;
current_str = s.substr(tmp_index_head, current_len); //取子字符串,验证是否为回文字符
while( ((long long)(tmp_index_tail-tmp_index_head)>=1) && (s[tmp_index_tail]==s[tmp_index_head]) ) {
tmp_index_head++;
tmp_index_tail--;
}
if( ((long long)(tmp_index_tail-tmp_index_head)==-1) || (tmp_index_tail-tmp_index_head==0) ){ //奇数 偶数个字符的情况
longest_len = current_len;
longest_str = current_str;
}
}
}
}
return longest_str;
}
};
2.2 动态规划
删除暴力解法中有很多重复的判断。很容易想到动态规划,时间复杂度O(n^2),空间O(n^2),动态规划方程如下:
- dp[i][j] 表示子串s[i…j]是否是回文
- 初始化:dp[i][i] = true (0 <= i <= n-1); dp[i][i-1] = true (1 <= i <= n-1); 其余的初始化为false
- dp[i][j] = (s[i] == s[j] && dp[i+1][j-1] == true)
在动态规划中保存最长回文的长度及起点即可
|
class Solution { public : string longestPalindrome(string s) { const int len = s.size(); if (len <= 1) return s; bool dp[len][len]; //dp[i][j]表示s[i..j]是否是回文 memset (dp, 0, sizeof (dp)); //初始化为0 int resLeft = 0, resRight = 0; dp[0][0] = true ; for ( int i = 1; i < len; i++) { dp[i][i] = true ; dp[i][i-1] = true ; //这个初始化容易忽略,当k=2时要用到 } for ( int k = 2; k <= len; k++) //外层循环:枚举子串长度 for ( int i = 0; i <= len-k; i++) //内层循环:枚举子串起始位置 { if (s[i] == s[i+k-1] && dp[i+1][i+k-2]) { dp[i][i+k-1] = true ; if (resRight-resLeft+1 < k) { resLeft = i; resRight = i+k-1; } } } return s.substr(resLeft, resRight-resLeft+1); } }; |
2.3 从中间向两边展开,时间复杂度O(n^2),空间O(1)
回文字符串显然有个特征是沿着中心那个字符轴对称。比如aha沿着中间的h轴对称,a沿着中间的a轴对称。那么aa呢?沿着中间的空字符''轴对称。所以对于长度为奇数的回文字符串,它沿着中心字符轴对称,对于长度为偶数的回文字符串,它沿着中心的空字符轴对称。对于长度为N的候选字符串,我们需要在每一个可能的中心点进行检测以判断是否构成回文字符串,这样的中心点一共有2N-1个(2N-1=N-1 + N)。检测的具体办法是,从中心开始向两端展开,观察两端的字符是否相同
class
Solution {
public
:
string longestPalindrome(string s) {
const
int
len = s.size();
if
(len <= 1)
return
s;
int
start, maxLen = 0;
for
(
int
i = 1; i < len; i++)
{
//寻找以i-1,i为中点偶数长度的回文
int
low = i-1, high = i;
while
(low >= 0 && high < len && s[low] == s[high])
{
low--;
high++;
}
if
(high - low - 1 > maxLen)
{
maxLen = high - low -1;
start = low + 1;
}
//寻找以i为中心的奇数长度的回文
low = i- 1; high = i + 1;
while
(low >= 0 && high < len && s[low] == s[high])
{
low--;
high++;
}
if
(high - low - 1 > maxLen)
{
maxLen = high - low -1;
start = low + 1;
}
}
return
s.substr(start, maxLen);
}
};
java
两侧比较:
public class LongestPalindromicSubString1 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(longestPalindrome1("babcbabcbaccba")); } public static String longestPalindrome1(String s) { int maxPalinLength = 0; String longestPalindrome = null; int length = s.length(); // check all possible sub strings for (int i = 0; i < length; i++) { for (int j = i + 1; j < length; j++) { int len = j - i; String curr = s.substring(i, j + 1); if (isPalindrome(curr)) { if (len > maxPalinLength) { longestPalindrome = curr; maxPalinLength = len; } } } } return longestPalindrome; } public static boolean isPalindrome(String s) { for (int i = 0; i < s.length() - 1; i++) { if (s.charAt(i) != s.charAt(s.length() - 1 - i)) { return false; } } return true; } }
动态规划:
public class LongestPalindromicSubString2 { public static String longestPalindrome2(String s) { if (s == null) return null; if(s.length() <=1) return s; int maxLen = 0; String longestStr = null; int length = s.length(); int[][] table = new int[length][length]; //every single letter is palindrome for (int i = 0; i < length; i++) { table[i][i] = 1; } printTable(table); //e.g. bcba //two consecutive same letters are palindrome for (int i = 0; i <= length - 2; i++) { //System.out.println("i="+i+" "+s.charAt(i)); //System.out.println("i="+i+" "+s.charAt(i+1)); if (s.charAt(i) == s.charAt(i + 1)){ table[i][i + 1] = 1; longestStr = s.substring(i, i + 2); } } System.out.println(longestStr); printTable(table); //condition for calculate whole table for (int l = 3; l <= length; l++) { for (int i = 0; i <= length-l; i++) { int j = i + l - 1; if (s.charAt(i) == s.charAt(j)) { table[i][j] = table[i + 1][j - 1]; if (table[i][j] == 1 && l > maxLen) longestStr = s.substring(i, j + 1); } else { table[i][j] = 0; } printTable(table); } } return longestStr; } public static void printTable(int[][] x){ for(int [] y : x){ for(int z: y){ //System.out.print(z + " "); } //System.out.println(); } //System.out.println("------"); } public static void main(String[] args) { System.out.println(longestPalindrome2("1263625"));//babcbabcbaccba } }