zoukankan      html  css  js  c++  java
  • leetcode

    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.

    求最长回文子串的问题

    个人思路:

    1,暴力破解,求出字符串的所有子串O(n2),再判断该子串是否为回文串O(n),并记录下最长的那个回文子串,总时间复杂度为O(n3),这么做会超时

    2,使用manacher算法,这个算法非常巧妙,时间复杂度和空间复杂度均为O(n),这里分享一个讲解该算法的文章:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824,接下来说说我对这个算法的理解:

      1,首先要明白,这个算法是在枚举中心点算法的基础上优化的,枚举中心点算法就是以每个字符为回文串的中心点,向左右两边扩张,以此获得一个最长的回文串,但要注意,该算法需要考虑奇数和偶数的情况,如"1221"和"121"

      2,在用这个算法计算前,需要对原串s做一个处理,用一个例子来说明好了,如串s = "12212321",处理之后变为串t = "^#1#2#2#1#2#3#2#1#$",之后便是对t进行操作,这么处理有两个原因:第一个原因是在字符之间插入'#'字符,用于统一考虑奇数和偶数的情况,如果回文子串是偶数的,则会以'#'字符为中心点,如果为奇数,则会以原串中的字符为中心点;第二个原因是在头部和尾部插入两个不同的特殊字符,避免处理越界的情况。

      3,设置一个数组P,用于记录以每个字符为中心点时的回文半径,即向右或者向左扩张的长度,这个长度不包括中心点,因此,该长度就是回文子串包含的非#字符的个数,也即有效的字符个数,比如,现在要计算P[5](t的起始字符从0开始),很明显,以t[5]为中心点的回文串是"#1#2#2#1#",向右扩张4个字符,则P[5] = 4,同时也等于该回文串实际的有效字符(1,2,2,1)的个数

      4,好了,要进入该算法的正题了,如果只是枚举中心点的算法,则每次P[i]都是从0开始递增,但在这里,我们会利用回文的性质,来加速这个过程,当我们要计算P[i]时,如果t[i]落在t[j]的回文半径中,则有很好的一个性质,看下图:

    先不用管3副图之间的差别,就看i,j,k这3个下标好了,i落在j的回文半径里,k是i相对于j的对称点,其实,回文串就是基于中心字符对称的,抓住这个性质,我们先抛出结论,P[i] >= min(P[k], j + P[j] - i),如何得出这个结论的呢?这就要分析上图的3中情况了

      第1种情况,k的回文范围还在j的回文范围中(除掉k-P[k] == j - P[j]的情况),此时根据j回文串的对称性和k回文串的对称性,可以得出P[i] >= P[k],那i回文串还能否往外扩张呢?假设可以,则根据i回文串和k回文串的对称性可以知道k回文串的范围就不只是此时的长度了,得出矛盾,所以P[i] = P[k]

      第2种情况,k的回文范围超出了j的回文范围,超出的那部分不在j回文串的保证范围内,因此P[i] >= j + P[j] - i,因为j + P[j] - i这段范围是得到保证的最大范围,那i回文串还能否往外扩张呢?假设可以,则根据i回文串和k回文串可以知道j回文串的范围就不只是此时的长度了,得出矛盾,所以P[i] = j + P[j] - i

      第3种情况,k的回文范围刚好在j的回文边界上,根据1的部分推论,有P[i] >= P[k],但由于此时k的回文边界与j的回文边界重合,i是可能往外扩张的,因为此时往外扩张不受到j回文串的制约了,这时需要在P[i] = P[k]的基础上,继续向外扩张来增长i回文串的长度

      当然,上面讨论的情况都是i落在了某个中心点j的回文范围内,如果i不在任何中心点的回文范围内,则必须从P[i] = 0开始,不断试探扩张来计算回文长度

      5,最后,需要把在t串中计算的长度和子串转成原串s中的长度和子串,这个比较容易,就不说了

    代码:

     1 #include<string>
     2 #include<vector>
     3 
     4 using namespace std;
     5 
     6 class Solution {
     7 public:
     8     string longestPalindrome(string s) {
     9         if (s.empty())
    10         {
    11             return s;
    12         }
    13 
    14         string T = preProcess(s);
    15         const int length = T.length();
    16         vector<int> P; //每个字符为中心点时的回文半径,不包括中心点自己,因此,回文半径即为该回文串实际包含的字符数
    17         P.resize(length);
    18         P[1] = 0;
    19         int C = 1, E = 1; //向前覆盖最大的回文串中心点下标和边界点下标
    20         int maxC = 1; //最大回文半径的中心点下标
    21 
    22         for (int i = 2; i < length - 2; ++i)
    23         {
    24             P[i] = i < E ? min(P[2 * C - i], E - i) : 0;
    25 
    26             while (T[i + P[i] + 1] == T[i - P[i] - 1])
    27             {
    28                 ++P[i];
    29             }
    30 
    31             if (i + P[i] > E)
    32             {
    33                 C = i;
    34                 E = i + P[i];
    35             }
    36 
    37             if (P[i] > P[maxC])
    38             {
    39                 maxC = i;
    40             }
    41         }
    42         
    43         return s.substr((maxC - P[maxC] - 1) / 2, P[maxC]);
    44     }
    45 
    46     string preProcess(string s)
    47     {
    48         string t = "^#";
    49         int length = s.length();
    50 
    51         for (int i = 0; i < length; ++i)
    52         {
    53             t.insert(t.end(), s[i]);
    54             t.insert(t.end(), '#');
    55         }
    56         t.insert(t.end(), '$');
    57 
    58         return t;
    59     }
    60 };
    View Code
  • 相关阅读:
    Ruby创始人谈Ruby的blocks和closure结构
    C语言字节对齐
    如今的开发者应了解哪些过去闻所未闻的新技能
    mongo下面总是缺少那么几个好用的工具试试这个吧MongoDB管理工具
    我们程序员为什么难晋升
    CMMI vs. Scrum vs. XP
    Rspec在Rails项目中的使用
    什么是Scrum?
    大型软件产品的敏捷案例 分享
    补充“为什么Scrum不行” (转自陈勇)
  • 原文地址:https://www.cnblogs.com/laihaiteng/p/3970889.html
Copyright © 2011-2022 走看看