Manacher算法

最长回文子串问题#

给定一个字符串,找出最长的回文子串,如"waabwswbfd",则最长子串为bwsb.

中心试探法#

最简单的方法,将每一个字符当成中心点,向两边扩展,找到最长的。
时间复杂度为O(n^2),太慢。

Manacher算法#

这个算法充分利用了字符串匹配问题的特殊性,大大减少了匹配次数,时间复杂度O(n)。
首先算法为了解决字符匹配中的奇偶问题,在每个字符之间插入了一个特别字符‘#’(字符串中不存在就可以)。并且为了数组不越界在首字符处插入了‘$’字符,原因在看完代码后自行思考。
改造后的数组如下:
"$#w#a#a#b#w#s#w#b#f#d#"
算法的关键在于使用了两个变量(也可以用一个变量),id和mx以及一个数组P。id为到i为止以每个字符为中心点的子串能匹配到的最长的一个中心点坐标。P[id]表示到当前为止可以向右的最长距离。mx为P[id] + id即到目前为止可以向右的最远的边界。
根据上面这三个值,有一个重要的结论,即当mx > i时P[i]>=min(P[2id - i], mx - i).
上面这个公式看起来很费解,下面分析一下。
令j = 2
id - i;找个例子画一下其实j指的位置是以id为中心点i所对应的位置(就是以id为中心点折叠过去的位置),当mx>i时候说明之前有一个位置以id的中心点的回文子串向右可以越过当前位置i,那么这个子串向左也一定包含对应点j,因为回文是对称的,如果以j为中心点的回文子串被完全包含在以id为中心点的回文子串中,那么以i为中心点的回文子串的长度最少是P[j]的长度。如果没有完全包含,即P[j]>mx-i那么位置在mx以外的字符由于还没匹配到,所以是未知的,只能等匹配到了才能知道是否为回文。
所以可以得到如上的公式。可以看出在这其中mx是必然递增的,即不会回溯。

#include                                                                                                                                                                          
#include
using namespace std;

const int N=300010;
int n, p[N];
char s[N], str[N];

#define _min(x, y) ((x)<(y)?(x):(y))

void kp()
{
    int i;
    int mx = 0;
    int id;
    for(i=n; str[i]!=0; i++)
        str[i] = 0; //ûÓÐÕâÒ»¾äÓÐÎÊÌâ¡£¡£¾Í¹ý²»ÁËural1297£¬±ÈÈçÊý¾Ý£ºababa aba
    for(i=1; i i )
            p[i] = _min( p[2*id-i], p[id]+id-i );
        else
            p[i] = 1;
        for(; str[i+p[i]] == str[i-p[i]]; p[i]++)
            ;
        if( p[i] + i > mx )
        {
            mx = p[i] + i;
            id = i;
        }
    }
}

void init()
{
 int i, j, k;
 str[0] = '$';
 str[1] = '#';
 for(i=0; ians)
    ans = p[i];
  printf("%d\n", ans-1);
 }
 return 0;
}  

你可能感兴趣的:(Manacher算法)