zoukankan      html  css  js  c++  java
  • Manacher Algorithm 最长回文子串

    问题:求一个字符串的最长回文子串的长度

     http://hihocoder.com/problemset/problem/1032

    1.Brute Force

    枚举子串(起点和终点),再判断子串是否回文串。时间复杂度O(n^3)

    2.稍优的算法

    枚举子串的中点,从中点向两侧扩展判断回文串。时间复杂度O(n^2)

    3.Manacher算法

    在前一种算法的基础上再加以改进,达到O(n)的时间复杂度

    (1)处理偶数长度的子串

    首先考虑一个问题:长度为奇数的字符串的中心是中间那个字符的位置,而长度为偶数的字符串的中心是中间两个字符串之间的“缝隙”。枚举偶数长度的回文子串的中点,似乎不好处理(如何能枚举一个“缝隙”的位置呢?)

    既然这样,设法做一种预处理,在原字符串s的间隙插入特殊字符,例如'#'(包括首尾),得到新字符串t

      例如:

      s:    abcba

      t:     #a#b#c#b#a#

    这样,枚举到'#'就表示枚举到偶数长度的子串的中点了

    (2)辅助变量

    回文半径:回文串最右字符与回文中心字符的距离。以t[i]为中心的回文子串t[l,r],其回文半径为r-i+1

      例如:aba的回文半径为2

    辅助数组len:len[i]表示字符串t中以第i个字符为中心的回文子串的回文半径

      可知len[i]-1就是该回文子串在原字符串s中的长度

      证明:

      首先,在字符串t中,该回文子串长度是2*len[i]-1,以#开头且以#结尾;不妨先“去掉”开头或结尾的一个#,剩下的部分长度是2*len[i]-2,其中一半是原串s中的字符,一半是#,所以该子串在原串s中的长度就是len[i]-1

    辅助变量pos和maxRight:maxRight是当前已知的所有回文子串的右端点能延伸到的最大值,取得这个最大值的回文串的中点是pos

    (3)计算len数组

    从左往右依次计算len[i],设j是i关于pos的对称点,有(j+i)/2 = pos,j = 2*pos - i(j<i,故len[j]是已知的)

    讨论i的位置与maxRight的相对关系:

      当i<=maxRight:

      考虑len[j],若len[j] <= maxRight-i+1,则len[i] = len[j];否则len[i]的最小值是maxRight-i+1(从maxRight+1开始向右还可能再延伸以t[i]为中心的回文串长度)

      当i>maxRight:

      此时len[i]无法如前所述地利用之前已经求得的信息了,故只能从t[i]像左右扩展寻找最长回文长度

    (4)时间复杂度分析

    maxRight只会一直向右推移,i>maxRight时,对于一个字符t[i],它影响maxRight,使maxRight向右推移,此时我们进行了回文串的左右匹配过程;但是当i<maxRight时,就能直接利用之前以求得的信息,[0,maxRight]范围内的字符都暂时不再需要参与匹配了。所以字符串t中的每一个字符(作为右端字符)只会参与一次匹配,故时间复杂度为O(n)。(考虑某字符可能先被作为某子串的右端字符参与匹配,之后又作为另一子串的左端字符参与匹配,那么每个字符也最多只参与2次匹配,这仍然是线性的)

    4.代码实现

     1 int Manacher(string &s)
     2 {
     3     string t="#";
     4     for(int i = 0; i < s.size(); ++i)
     5         t += s[i], t += '#';
     6 
     7     int len[t.size()];
     8     int maxLen = 0;
     9     int pos = 0, maxRight = 0;
    10     for(int i = 0; i < t.size(); ++i)
    11     {
    12         len[i] = i<=maxRight? min(len[2*pos-i], maxRight-i+1) : 1;
    13         while(i-len[i]>=0 && i+len[i]<t.size() && t[i-len[i]]==t[i+len[i]])
    14             ++len[i];
    15         if(i+len[i] > maxRight)
    16             pos = i, maxRight = i+len[i]-1;
    17         maxLen=max(maxLen, len[i]-1);
    18     }
    19     return maxLen;
    20 }

    PS:如果要求最长回文串,那么在过程中记录下在字符串t中的maxLen对应的idx和len[idx]即可。(最长回文子串是t[idx-len[idx]+1, idx+len[idx]-1]中不为'#'的子序列组成的字符串)

  • 相关阅读:
    Nginx 部署多个 web 项目(虚拟主机)
    Nginx 配置文件
    Linux 安装 nginx
    Linux 安装 tomcat
    Linux 安装 Mysql 5.7.23
    Linux 安装 jdk8
    Linux 安装 lrzsz,使用 rz、sz 上传下载文件
    springMVC 拦截器
    spring 事务
    基于Aspectj 注解实现 spring AOP
  • 原文地址:https://www.cnblogs.com/junjie_x/p/7668714.html
Copyright © 2011-2022 走看看