[hihocoder1032]最长回文子串

这里只是对于“最长回文子串”算法的简单定义与实现代码,具体请参照hihocoder的官网。

问题描述

回文串的定义是正读与反读相同;子串的定义是任意连续字符串;求给定字符串的最长回文子串的长度。详情请参照hihocoder的官网,上面也有详细的算法介绍。

实现思路

1. 最简单的方法

从最长长度递减,检查所有的子串判断是否是回文子串,最差时间复杂度是O(n^2)。

2. 枚举字符串中心

以字符串中心的较短子串非回文,则较长子串必定非回文。

3. 利用之前的信息得到回文子串的最短长度

现在要求的是中心index=i的最长回文子串,如果index=j的最长回文子串长度maxPal[j]的右边界大于i(即maxPal[j]/2+j>i),那么可以知道对于任意2*i-maxPal[j]/2-j < k < maxPal[j]/2+j-i,有str[i+k] == str[2*j-i+k]。所以可以利用2*j-i处的最大回文子串的长度,得到当前处的回文子串长度的最小值。具体公式为maxPal[i]/2 >= min{j+maxPal[j]/2-i,maxPal[2*j-i]/2}。

此处不需要枚举所有的j以取得最大值,只需要计算j+maxPal[j]最大的j即可。

4. 奇偶串的处理

上诉方法只能处理长度为奇数的回文子串,通过拓展原串(即在字符前后都插入特殊字符),可以把算法拓展到偶数长度的回文子串。

这样得到的回文子串的长度需要去除特殊字符的部分。首先明确的是拓展后的字符串的最长回文子串首尾都是特殊字符,否则可以在两端加入特殊字符使得长度+2。这样就可以通过maxPal[i]/2的奇偶性反应i的奇偶性(/i对应的是否为特殊字符)。如果i对应的是特殊字符(即i为偶数,即maxPal[i]/2为偶数),则得到就是偶数的回文子串;反之依然。

代码如下

int maxPalindrome(char *str,int *maxPal,char *tmp_str){
    int len = strlen(str);

    //expand string
    for (int i = 0;i < len;++i)
        tmp_str[2*i+1] = str[i];
    
    /**
    max_j表示j+maxPal[j]/2最大的j;
    result表示最长回文子串的长度(没有去特殊符号);
    pal是暂时使用的变量,最后等于maxPal[i]/2+1;
    tmp_start表示求得的当前位置的最短回文子串的长度;
    **/
    int max_j = 0,result = 1,pal,tmp_start;
    len = len*2+1;
    maxPal[0] = 1;
    for (int i = 1;i < len;++i){
        //求当前位置的最短回文子串长度
        tmp_start = 0;
        if (max_j+maxPal[max_j]/2-i > 0)
            tmp_start = max_j+maxPal[max_j]/2-i;
        if (2*max_j-i >= 0 && tmp_start > maxPal[2*max_j-i]/2)
            tmp_start = maxPal[2*max_j-i]/2;

        //从当前位置逐渐增加子串长度,判断是否回文,最后得到pal=maxPal[i]/2+1;
        for (pal = tmp_start+1;i-pal > -1 && i+pal < len && tmp_str[i+pal] == tmp_str[i-pal];++pal);
        maxPal[i] = pal*2-1;

        if (maxPal[i] > result)
            result = maxPal[i];
        if (i+pal-1 > max_j+maxPal[max_j]/2)
            max_j = i;
    }
    
    //result/2&1用于区分奇偶子串,第一部分求取除中心外的长度,第二部分用于判断中心是否为特殊符号,决定是否加1
    return result/2/2*2+((result/2)&1);
}



你可能感兴趣的:(hihocoder)