• Leetcode5>最长回文子串


    题目给定一个字符串s,找出s中的最长回文子串;

    暴力法,DP法, 中心扩展法,manacher算法

    解法一:暴力法

    遍历字符串S的每一个子串,去判断这个子串是不是回文,是回文的话看看长度是不是比最大的长度maxlength大。遍历每一个子串的方法要O(n^2),判断每一个子串是不是回文的时间复杂度是O(n),所以暴利方法的总时间复杂度是O(n^3)。

     1 public static String findLongestPalindrome(String s){
     2         int len = s.length(); // 字符串长度
     3         int maxlength = 0;  // 最长回文字符串长度
     4         int start = 0; // 回文开始的地方
     5         for(int i = 0; i < len; i++){
     6             for(int j = i + 1; j < len; j++){
     7                 int index1 = 0;
     8                 int index2 = 0;
     9                 // 对每个子串都从两边开始向中间遍历
    10                 for(index1 = i, index2 = j; index1 < index2; index1 ++, index2--){
    11                     if(s.charAt(index1) != s.charAt(index2))
    12                         break;
    13                 }
    14                 // 若index1=index2,表示串是类似于abcba这种类型;若大于,则是abccba这种类型
    15                 if(index1 >= index2 && j - i > maxlength){
    16                     maxlength = j - i + 1;
    17                     start = i;
    18                 }
    19             }
    20 
    21         }
    22         if(maxlength > 0)
    23             return s.substring(start, start + maxlength);
    24         return null;
    25 
    26     }

    解法二: 动态规划

    回文字符串的子串也是回文,比如P[i,j](表示以i开始以j结束的子串)是回文字符串,那么P[i+1,j-1]也是回文字符串。这样最长回文子串就能分解成一系列子问题了。这样需要额外的空间O(N^2),算法复杂度也是O(N^2)。

    首先定义状态方程和转移方程:

    P[i,j]=false:表示子串[i,j]不是回文串。P[i,j]=true:表示子串[i,j]是回文串。

    P[i,i]=true:当且仅当P[i+1,j-1] = true && (s[i]==s[j])

    否则p[i,j] =false;

     1 public static String findLongestPalindrome1(String s){
     2         int len = s.length();
     3         int start = 0;
     4         int maxlength = 0;
     5         boolean p[][] = new boolean[s.length()][s.length()];
     6         // 子串长度为1和为2的初始化
     7         for(int i = 0; i < len; i++){
     8             p[i][i] = true;
     9             if(i < len - 1 && s.charAt(i) == s.charAt(i + 1)){
    10                 p[i][i + 1] = true;
    11                 start = i;
    12                 maxlength = 2;
    13             }
    14         }
    15         // 使用上述结果可以dp出子串长度为3~len -1的子串
    16         for(int strlen = 3; strlen < len; strlen ++){
    17             for(int i = 0; i <=len - strlen; i++){
    18                 int j = i + strlen - 1; // 子串结束的位置
    19                 if(p[i + 1][j - 1] && s.charAt(i) == s.charAt(j)){
    20                     p[i][j] = true;
    21                     maxlength = strlen;
    22                     start = i;
    23                 }
    24             }
    25         }
    26         if(maxlength > 0)
    27             return s.substring(start, start + maxlength);
    28         return null;
    29     }

    解法三:中心扩展法

    中心扩展就是把给定的字符串的每一个字母当做中心,向两边扩展,这样来找最长的子回文串。算法复杂度为O(N^2)。
    但是要考虑两种情况:
    1、像aba,这样长度为奇数。
    2、想abba,这样长度为偶数。
     1 public static String findLongestPalindrome2(String s){
     2         int len = s.length();
     3         int maxlength = 0;
     4         int start = 0;
     5         // 类似于aba这种情况,以i为中心向两边扩展
     6         for(int i = 0; i < len; i++){
     7             int j = i - 1;
     8             int k = i + 1;
     9             while(j >= 0 && k < len && s.charAt(j) == s.charAt(k)){
    10                 if(k - j + 1 > maxlength){
    11                     maxlength = k - j + 1;
    12                     start = j;
    13                 }
    14                 j --;
    15                 k ++;
    16             }
    17         }
    18         // 类似于abba这种情况,以i,i+1为中心向两边扩展
    19         for(int i = 0; i < len; i++){
    20             int j = i;
    21             int k = i + 1;
    22             while(j >= 0 && k <len && s.charAt(j) == s.charAt(k)){
    23                 if(k - j + 1 > maxlength){
    24                     maxlength = k - j + 1;
    25                     start = j;
    26                 }
    27                 j --;
    28                 k ++;
    29             }
    30         }
    31         if(maxlength > 0)
    32             return s.substring(start, start + maxlength);
    33         return null;
    34     }

    解法四:Manacher算法

    Manacher法只能解决例如aba这样长度为奇数的回文串,对于abba这样的不能解决,于是就在里面添加特殊字符。我是添加了“#”,使abba变为a#b#b#a。这个算法就是利用已有回文串的对称性来计算的,具体算法复杂度为O(N)

    详细规则可参考另一篇博客http://blog.sina.com.cn/s/blog_9ca3f6e70102wsb0.html

     1 public static String findLongestPalindrome3(String s) {
     2         if(s == null || s.length() < 1)
     3             return "";
     4         String str = dealWithS(s);  // 处理一下s,即将给字符串s的中间加上特殊字符,这样无论对于奇数字符还是偶数字符可以做同样的处理
     5         int[] res = new int[str.length()];
     6         int R = 0; // 当前所能扩展的半径
     7         int C = 0; // C位置的半径为R
     8         int maxC= 0; // 最长的半径的位置
     9         res[0] = 0;
    10         for(int i = 1; i < str.length(); i++)
    11         {
    12             int j = 2 * C - i;  // i点的对称点
    13             if(j >= 0 && res[j] < R - i)  // 对称点存在且对称点的回文半径在C的回文中
    14             {
    15                 res[i] = res[j];
    16             }
    17             else  // 否则,需要根据i点一点一点的计算
    18             {
    19                 int k = 1;
    20                 while(R + k < str.length() && 2 * i - R - k >= 0)
    21                 {
    22                     if(str.charAt(R + k) == str.charAt(2 * i - R - k))
    23                         k ++;
    24                     else
    25                         break;
    26                 }
    27                 res[i] = R -i + k - 1;
    28                 if(res[i] + i > R)
    29                 {
    30                     R = res[i] + i;
    31                     C = i;
    32                 }
    33             }
    34 
    35             maxC = res[maxC] > res[i] ? maxC : i;  // maxC保存的是回文半径最大的那个点的位置
    36         }
    37         String subStr = str.substring(maxC - res[maxC], maxC + res[maxC] + 1);
    38         StringBuffer sb = new StringBuffer();
    39         for(int i = 0; i < subStr.length(); i++)
    40         {
    41             if(subStr.charAt(i) != '#')
    42                 sb.append(subStr.charAt(i));
    43         }
    44         return sb.toString();
    45     }
    46     public static String dealWithS(String s)  // 将原字符串进行处理
    47     {
    48         StringBuffer sb = new StringBuffer();
    49         sb.append("#");
    50         for(int i = 0; i < s.length (); i++)
    51         {
    52             sb.append(s.charAt(i));
    53             sb.append("#");
    54         }
    55         return sb.toString();
    56     }

     

     
  • 相关阅读:
    Business
    Triple Inversions
    protobuf
    16.04 ubuntu python3.6 install
    1.安装
    Tutorial2
    Tutorial1
    geometry_msgs的ros message 类型赋值
    UBUNTU QQ/TIM的救星
    ubuntu17.10升级到ubuntu18.04 LTS
  • 原文地址:https://www.cnblogs.com/leavescy/p/5878336.html
Copyright © 2020-2023  润新知