题目描述:给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
解析:最长回文字符串就是正过来和反过来一样的字符串,比如上海自来水来自海上,小明到操场操到天明。。
方法一:从最暴力的方法入手,直接遍历所有的子字符串
1 class Solution { 2 public: 3 string longestPalindrome(string s) { 4 //暴力解法 5 int length = s.length(); 6 int max = 0; 7 string res = ""; 8 for(int i = 0; i < length; i++){ 9 for(int j = i + 1; j <= length; j++){ 10 string tmp = string(s, i, j - i); 11 //cout << tmp << endl; 12 if(isPalindrome(tmp) && tmp.length() > max){ 13 //cout << tmp << endl; 14 max = tmp.length(); 15 res = tmp; 16 } 17 } 18 } 19 return res; 20 21 } 22 23 bool isPalindrome(string s){ 24 for(int i = 0; i < s.length() / 2; i++){ 25 if(s[i] != s[s.length() - i - 1]){ 26 return false; 27 } 28 } 29 return true; 30 } 31 };
分析:遍历子字符串需要n(n - 1)/2,复杂度为O(n^2),每次判断是否为回文字符串的复杂度为O(n),所以,此算法复杂度为O(n^3);
方法二:通过字符串翻转,然后找到最长公共字串,再判断是否是回文字串
1 class Solution { 2 public: 3 string longestPalindrome(string s) { 4 string s_resvere = s; 5 reverse(s_resvere.begin(), s_resvere.end()); // 翻转字符串 6 return findlongestSubStr(s, s_resvere, s.length()); 7 } 8 9 string findlongestSubStr(string s1, string s2, int length){ 10 bool check = 0; 11 string res; 12 for(int step = length; step > 0; step--){ 13 for(int i = 0; i + step - 1 < length; i++){ 14 string tmp = string(s1, i, step); //定义一个临时变量,从i下标开始,往后加step个字符串 15 if(s2.find(tmp) != -1){ 16 string rtmp = tmp; 17 reverse(rtmp.begin(), rtmp.end()); 18 if(rtmp == tmp){ 19 res = tmp; 20 check = 1; 21 break; 22 } 23 } 24 } 25 if(check) break; 26 } 27 return res; 28 } 29 };
分析:此算法的时间复杂度仍然为o(n^3);
方法三:动态规划,通过动态规划存放之前比较的结果,从而将时间复杂度缩减至O(n^2)
1 class Solution { 2 public: 3 string longestPalindrome(string s) { 4 int length = s.length(); 5 if(length <= 1) return s; 6 int max = 1; //代表最大回文串长度 7 int start = 0; //代表回文串的起始下标 8 int dp[1000][1000] = {0}; // 定义一个二维数组,初始化为0,dp[i][j]=1代表从 从i 到 j闭区间内为回文串 9 for(int i = 0; i < length; i++){ // 进行初始化,如果为dp[i][i]一定为回文串,置为1,同样,两个连续的相同的字符也能组成一个回文串 10 dp[i][i] = 1; 11 if(i < length - 1 && s[i] == s[i + 1]) { 12 start = i; 13 max = 2; 14 dp[i][i + 1] = 1; 15 } 16 } 17 for(int size = 3; size <= length; size++){ //之前从2 判断过了, 所以从3开始进行判断 18 int right; 19 for(int left = 0; left + size - 1< length; left++){ 20 right = left + size - 1; 21 if(s[left] == s[right] && dp[left + 1][right - 1] == 1){ // 如果左边等于右边,而且中间还是回文串,那么这个[left,right]一定是回文串 22 dp[left][right] = 1; 23 max = size; 24 start = left; 25 } 26 } 27 } 28 return s.substr(start, max); 29 } 30 };
方法四:中心扩展算法,通过中心扩展,减少相应的迭代步骤,降低时间复杂度。具体实现:一个回文串的中心可能是一个字符,也可能是两个字符,如果以一个字符为中心,有n个中心,以两个字符为中心,就有n-1个中心,所以一共有2n-1个中心;
1 class Solution { 2 public: 3 string longestPalindrome(string s) { 4 int length = s.length(); 5 if(length <= 1) return s; 6 int max_res = 1; 7 int start = 0; 8 for(int i = 0; i < length; i++){ 9 int max1 = maxLength(s, i, i); 10 int max2 = maxLength(s, i, i + 1); 11 if(max_res < max(max1, max2)){ 12 max_res = max(max1, max2); 13 start = i - (max_res - 1) / 2; 14 //cout << "max = "<< max_res << " " <<" start = " << start << " i = " << i <<endl; 15 } 16 } 17 return s.substr(start, max_res); 18 } 19 20 private: 21 int maxLength(string s, int left, int right){ 22 while(left >= 0 && right < s.length() && s[left] == s[right]) { 23 left--; 24 right++; 25 } 26 return right - left - 1; 27 } 28 };