1. 程式人生 > >資料結構演算法題/最長迴文子串

資料結構演算法題/最長迴文子串

迴文表示字串正向和反向是相同的。例如a, aba, abccba

一、暴力法

最容易想到的就是暴力破解,求出每一個子串,之後判斷是不是迴文,找到最長的那個。

求每一個子串時間複雜度O(N^2), 判斷子串是不是迴文O(N),兩者是相乘關係,所以時間複雜度為O(N^3)。

二、動態規劃

下面介紹動態規劃的方法,使用動態規劃可以達到最優的 O(n2) 複雜度。

  令 dp[i][j] 表示 S[i] 至 S[j] 所表示的子串是否是迴文子串,是則為 1,不是則為 0。這樣根據 S[i] 是否等於 S[j] ,可以把轉移情況分為兩類:

  1.  若 S[i] == S[j],那麼只要 S[i+1] 至 S[j-1] 是迴文子串,S[i] 至 S[j] 就是迴文子串;如果S[i+1] 至 S[j-1] 不是迴文子串,則 S[i] 至 S[j] 也不是迴文子串。
  2.  若 S[i] != S[j],那麼 S[i] 至 S[j] 一定不是迴文子串。    

  根據遞推寫法從邊界出發的原理,注意到邊界表示的是長度為 1 和 2 的子串,且每次轉移時都對子串的長度減了 1,因此不妨考慮按子串的長度和子串的初始位置進行列舉,即第一遍將長度為 3 的子串的 dp 值全部求出,第二遍通過第一遍結果計算出長度為 4 的子串的 dp 值 

public class LongestPalindromicSubstring {
    public String longestPalindrome(String s)
    {
        int n = s.length();
        boolean dp[][] = new boolean[n][n];

        int maxlen = 1;     //儲存最長迴文子串長度
        int start = 0;      //儲存最長迴文子串起點
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j <= i; ++j)
            {
                if(i - j < 2)//包含了i=j和i-j=1的情況
                {
                    dp[j][i] = (s.charAt(i) == s.charAt(j));
                }
                else
                {
                    dp[j][i] = (s.charAt(i) == s.charAt(j) && dp[j + 1][i - 1]);
                }

                if(dp[j][i] && maxlen <= i - j + 1)//j到i之間的子串是迴文子串
                {
                    maxlen = i - j + 1;
                    start = j;
                }
            }
        }
        return s.substring(start, start + maxlen);
    }

    public static void main(String[] args) {
        LongestPalindromicSubstring longestPalindromicSubstring = new LongestPalindromicSubstring();
        System.out.println(longestPalindromicSubstring.longestPalindrome("QATZJUJZTA"));
    }
}

三、中心擴充套件法

中心擴充套件就是把給定的字串的每一個字母當做中心,向兩邊擴充套件,這樣來找最長的子迴文串。演算法複雜度為O(N^2)。
需要考慮兩種情況:
長度為奇數的迴文串,比如a, aba, abcba
長度為偶數的迴文串,比如aa, abba

public String longestPalindrome2(String s)
{
    int len = s.length();
    int maxlen = 1;
    int start = 0;

    for(int i = 0; i < len; i++)//求長度為奇數的迴文串
    {
        int j = i - 1, k = i + 1;//j和k分別是i的兩邊
        while(j >= 0 && k < len && s.charAt(j) == s.charAt(k))
        {
            if(k - j + 1 > maxlen)
            {
                maxlen = k - j + 1;
                start = j;
            }
            j--;
            k++;
        }
    }

    for(int i = 0; i < len; i++)//求長度為偶數的迴文串
    {
        int j = i, k = i + 1;//奇數和偶數的區別在於j的初始值
        while(j >= 0 && k < len && s.charAt(j) == s.charAt(k))
        {
            if(k - j + 1 > maxlen)
            {
                maxlen = k - j + 1;
                start = j;
            }
            j--;
            k++;
        }
    }
    return s.substring(start, start + maxlen);
}

四、Manacher演算法

Manacher演算法的時間複雜度為O(N),具體可參考:

https://blog.csdn.net/qq_32354501/article/details/80084325
https://www.cnblogs.com/grandyang/p/4475985.html

https://www.jianshu.com/p/c82cada7e5b0

https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes30.html