回文串算法(中心扩展法、Manacher算法)

题目链接:https://leetcode-cn.com/problems/longest-palindromic-substring/submissions/
题目:返回给定串的最长回文子串。


为了方便分析,用字符 '#’ 表示空字符,那么对于字符串 s = "aabcdefg",可以表示成 _s = "#a#a#b#c#d#e#f#g#"。显然,对于长度为n的字符串s,可能的回文串的中心有n + (n + 1) = 2 * n + 1个,即原字符数加#字符数。

1.中心扩展法

遍历每一个可能的中心点,以该中心点为基础,生成回文串,即若两末端字符相等,则回文串向两端生长,直到两末端字符不相等,结束生长,得到以该点为中心的最长回文串。如此可以得到每个可能中心点的最长回文串,选取最长的返回。
代码:

class Solution {
    public String longestPalindrome(String s) {
    	return centerExpand(s);
    }
    public String centerExpand(String s){
    	//特判
    	if(s == null || s.length() == 0)return "";
        int start, end;
        start = end = 0;
        //遍历所有可能的中心点
        for(int i = 0; i < s.length(); i++){
        	//以(i, i)为两端端点生长,和以(i, i + 1)为两端端点生长,选取最长的
            int len = Math.max(expandString(s, i, i), expandString(s, i, i + 1));
            //更新记录的最长串的始末点
            if(end - start + 1 < len){
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }
    //在s中从i到j已经是回文串,试图让该回文串继续生长至最长,返回长度
    public int expandString(String s, int i, int j){
        while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
            i--;
            j++;
        }
        return j - i - 1;
    }
}

2.Manacher算法

我觉得这个算法是对中心扩展法的一个改进。中心扩展法遍历每一个可能的中心,并求出以该中心代表的最长回文串,具体做法是让该中心向两边生长至不能生长为止。Manacher算法同样是如此。

改进在于,中心扩展法的初始中心是一个字符,要么是原字符串中的一个字符,要么是‘#’(空字符),而Manacher算法可能是这样,也可能是一个有几个字符的回文串。所以,区别就是,Manacher算法在调用expandString函数时,参数可能不是(s, i, i),而是(s, l, r),问题变成了,你怎么知道s中从l到r是以i为中心的回文串?

起初,对于一个中心i,我们知道i到i是以i为中心的回文串,所以我们有了中心扩展算法。现在,对于一个中心i,我们知道l到r是以i为中心的回文串,所以我们有了Manacher算法,这两种算法的核心思想都一样,如果明白第一种做法,那明白第二种做法就只需要明白l和r怎么求。

现在我们来弄明白怎么求l和r
首先假设我们用中心扩展算法求解求了一半,如图,用线段表示字符串 _s:

回文串算法(中心扩展法、Manacher算法)_第1张图片
s1和s2都是已经求出的回文串,s2包含在s1的左半部分,显然,s1右半部分有一个s2的翻转串,又因为s2是回文串,翻转后依然是s2,假设翻转过去的s2的中心点刚好是i,那么翻转过去的s2左右端点下标就是我们需要的l和r

以l到r为中心去扩展,显然比以i到i为中心去扩展节约时间。

具体做法是,用C记录s1的中心下标,R表示s1的最右端下标,这样就可以唯一表示一个s1,显然,R越大越好,所以每次求出一个最长回文串,就维护一下R和C。为了保存s2之流的信息,我们开辟一个数组radius[i],radius[i]代表以i为中心的最长回文串长度的一半,显然中心扩展算法radius[i]初始全为1,而Manacher算法初始化为Math.min(radius[2 * C - i], R - i + 1)或者1,接着借radius[i],求出l和r
代码:

class Solution {
    public String longestPalindrome(String s) {
        return manacher(s);
    }

    public String manacher(String s){
    	//特判
        if(s == null || s.length() == 0) return "";
        //_s是插满了'#'的s
        String _s = manacherString(s);
        
        int[] radius = new int[_s.length()];
        int start = 0;
        int end = 0;
        int R = -1;
        int C = -1;
        //遍历每一个中心点
        for(int i = 0; i < radius.length; i++){
        	//初始化radius[i]
            radius[i] = R > i ? Math.min(radius[2 * C - i], R - i + 1) : 1;
            //l = i - radius[i] + 1    r =  i + radius[i] - 1
            int len = expandString(_s, i - radius[i] + 1, i + radius[i] - 1);
            //更新radiu[i]
            radius[i] = Math.max(radius[i], len / 2 + 1);
            //更新最长回文串的始末点
            if(end - start + 1 < len){
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
            //更新表示s1的C和R
            if(i + radius[i] - 1 > R){
                R = i + radius[i] - 1;
                C = i;
            }
        }
        
		//把最长回文串中的字符'#'去掉再返回     ps:要是用python我会写这种又臭又长的代码?
        StringBuffer ans = new StringBuffer();
        for(int i = start; i <= end; i++){
            if(_s.charAt(i) != '#'){
                ans.append(_s.charAt(i));
            }
        }
        return ans.toString();
    }
    //同上
    public int expandString(String s, int i, int j){
        while(i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)){
            i--;
            j++;
        }
        return j - i - 1;
    }
    //在字符串s的缝隙里插入字符'#',返回       ps:要是用python我会写这种又臭又长的代码?
    public String manacherString(String s){
        StringBuffer _s = new StringBuffer();
        _s.append("#");
        for(int i = 0; i < s.length(); i++){
            _s.append(s.charAt(i));
            _s.append("#");
        }
        return _s.toString();
    }
}

你可能感兴趣的:(回文串算法(中心扩展法、Manacher算法))