最长回文子字符串问题

来源:互联网 发布:淘宝美工培训班学费 编辑:程序博客网 时间:2024/05/21 07:02

Longest Palindromic Substring最长回文子字符串问题

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.动态规划

提示:第i个字符结尾的最长回文字符串

java代码

public static String longestPalindrome(String s) {    String maxsubstring="";    int lastlength=0;    for(int i=0;i<s.length();i++){        char a=s.charAt(i);        if(i-lastlength-1>=0){            if(a==s.charAt(i-lastlength-1)){                maxsubstring=lastlength+2>maxsubstring.length()?s.substring(i-lastlength-1,i+1):maxsubstring;                lastlength+=2;            }else{                for(int stt=i-lastlength;stt<=i;stt++){                    if(isPalindromeString(s,stt,i)){                         maxsubstring=i-stt+1>maxsubstring.length()?s.substring(stt,i+1):maxsubstring;                         lastlength=i-stt+1;                         break;                    }                }            }        }else{            for(int j=0;j<=i;j++){                if(isPalindromeString(s,j,i)){                    maxsubstring=i-j+1>maxsubstring.length()?s.substring(j,i+1):maxsubstring;                    lastlength=i-j+1;                    break;                }            }        }    }    return maxsubstring;}public static boolean isPalindromeString(String s,int st,int e){    int n=(st+e)/2;    int l=(st+e)%2;    boolean re=true;    while(re&&st<=n){        if(s.charAt(st)!=s.charAt(2*n+l-st)){           re=false;           break;        }        st++;    }    return re;}

2.中心扩展法

回文字符串显然有个特征是沿着中心那个字符轴对称。比如aha沿着中间的h轴对称,a沿着中间的a轴对称。那么aa呢?沿着中间的空字符”轴对称。

for i=1 to s.length

expand(s,i,i);//以第i个字符扩展

expand(s,i,i+1);//第i个字符后的空字符扩展

3.Manacher算法

由于回文串的长度可奇可偶,比如”bob”是奇数形式的回文,”noon”就是偶数形式的回文,马拉车算法的第一步是预处理,做法是在每一个字符的左右都加上一个特殊字符,比如加上’#’,那么

bob –> #b#o#b#

noon –> #n#o#o#n#

这样做的好处是不论原字符串是奇数还是偶数个,处理之后得到的字符串的个数都是奇数个,这样就不用分情况讨论了,而可以一起搞定。接下来我们还需要和处理后的字符串t等长的数组p,其中p[i]表示以t[i]字符为中心的回文子串的半径,若p[i] = 1,则该回文子串就是t[i]本身,那么我们来看一个简单的例子:

# 1 # 2 # 2 # 1 # 2 # 2 #1 2 1 2 5 2 1 6 1 2 3 2 1

由于第一个和最后一个字符都是#号,且也需要搜索回文,为了防止越界,我们还需要在首尾再加上非#号字符,实际操作时我们只需给开头加上个非#号字符,结尾不用加的原因是字符串的结尾标识为’\0’,等于默认加过了。通过p数组我们就可以找到其最大值和其位置,就能确定最长回文子串了,那么下面我们就来看如何求p数组,需要新增两个辅助变量mx和id,其中id为最大回文子串中心的位置,mx表示其半径,这个算法的最核心的一行如下:

p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;//以i字符为中心的最短半径

可以这么说,这行要是理解了,那么马拉车算法基本上就没啥问题了,那么这一行代码拆开来看就是

如果mx > i, 则 p[i] = min(p[2 * id - i], mx - i)

否则, p[i] = 1

当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j],见下图。

当 P[j] >= mx - i 的时候,以S[j]为中心的回文子串不一定完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] >= mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了。

对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了。

参见如下实现代码:

string Manacher(string s) {// Insert '#'string t = "$#";for (int i = 0; i < s.size(); ++i) {    t += s[i];    t += "#";}// Process tvector<int> p(t.size(), 0);int mx = 0, id = 0, resLen = 0, resCenter = 0;for (int i = 1; i < t.size(); ++i) {    p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;    while (t[i + p[i]] == t[i - p[i]]) ++p[i];//判断实际以i字符为中心的回文半径    if (mx < i + p[i]) {        mx = i + p[i];        id = i;    }    if (resLen < p[i]) {        resLen = p[i];        resCenter = i;    }}return s.substr((resCenter - resLen) / 2, resLen - 1);

}

0 0
原创粉丝点击