【LeetCode】劍指DP:5. Longest Palindromic Substring 最長迴文子串

一、概述

輸入一個字符串,輸出它的最長迴文子串。

經典DP問題。爲什麼突然想要做DP了呢?因爲最近這幾次打Contest,每次第三題和第四題必定是DP,不用DP做不出來那種。所以就自閉了。有的是自己寫了半天一直TLE才後知後覺需要DP。因此痛定思痛,要開始學一下DP。拿之前沒做過的DP題目來練手。因爲是新學習的,所以就先不糾結時空複雜度了,能做得出來就好。

二、分析

最愚蠢的方法就是先看第一個,然後從第一個開始看到最後一個,找出最長的;然後從第二個開始看到最後,需要O(n^3)的時間。使用DP則僅需要O(n^2)的時間。那麼如何使用DP呢?

DP在思考上的核心是找到遞推公式,也就是我現在已經知道一個樣例是對的了,那麼在這個樣例上加上什麼條件可以得到下一個樣例呢?以此題爲例,假設我們已經知道字符串的s[3,5]是一個迴文串,那麼加上什麼條件可以得到下一個迴文串?看字符串的2和6,如果s[2]==s[6],那麼就找到了下一個迴文串。

也就是說,想知道s[i,j]是不是迴文串,我們需要知道s[i+1,j-1]是不是,同時要判斷s[i]和s[j]是否相等。

遞歸公式需要給出前兩項,因此我們需要先手動算出長度爲1和長度爲2的所有子串是否爲迴文串,然後其餘長度的在後面遞推就可以得到了。如下圖所示:

DP在實現上的核心是如何通過s[i,j]找到s[i+1,j-1]。也就是如何實現遞推關係。本題所使用的是二維DP表,以babad爲例,寫下來DP表如下:

從上圖中我們可以很輕鬆的得到遞推關係的實現:

dp[i][j]=(dp[i-2][j+1])&&(s[j]==s[j+i]);

注意這裏,i+1是子串長度,j是子串最左端元素的下標。

從而整體代碼如下:

class Solution {
public:
    string longestPalindrome(string s) {
        int dp[1010][1010]={0};
        if(s.size()==0)
        {
            return "";
        }
        int max_i,max_j;
        for(int i=0;i<s.size();++i)
        {
            int flag=0;
            for(int j=0;j<s.size()-i;++j)
            {
                if(i<2)
                {
                    dp[i][j]=(s[j]==s[j+i]);
                    if(dp[i][j]==1&&flag==0)
                    {
                        max_i=i;
                        max_j=j;
                        flag=1;
                    }
                }
                else
                {
                    dp[i][j]=(dp[i-2][j+1])&&(s[j]==s[j+i]);
                    if(dp[i][j]==1&&flag==0)
                    {
                        max_i=i;
                        max_j=j;
                        flag=1;
                    }
                }
            }
        }
        return s.substr(max_j,max_i+1);
    }
};

三、總結

最長迴文子串還有更好的Manacher算法,我的算法也還有很大的優化空間。但是這是最簡單最直觀的一種實現了。優化的的問題,等到我對DP熟稔之後再說也不遲。DP給我的感覺,emmmm,類似數學歸納法?先是寫好最基本的情況,然後一層一層遞歸,得到一般的情況。有一說一,當時高中我數學歸納法就不怎麼樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章