球球速刷LC-區間DP

最長迴文子串
注意子串需要判斷兩端之間的子串是否是迴文串

class Solution {
public:
    string longestPalindrome(string s) {
        if(s.size() <=1) return s;
        if(s.size() == 2 && s[0]==s[1]) return s;
        if(s.size()==3 &&s[0]==s[2]) return s;
        
        int m=s.size();
        bool dp[m][m];
        for(int i=0;i<m;++i){
            for(int j=0;j<m;++j){
                dp[i][j] = false;
            }
        }
        
        int max_len =0;
        int max_s_start =0;
        for(int i=0;i<m;++i){
            for(int j=i;j>=0;--j){
                if(i==j) {
                    dp[j][i]=true;
                }else if(s[i] == s[j]){
                    if(j+1 == i) dp[j][i] =true;
                    else if(j+1<=i-1 && dp[j+1][i-1]) dp[j][i]=true;
                    else dp[j][i]=false;
                }else{
                    dp[j][i]=false;
                }
                if(dp[j][i]){
                    if(i-j+1 > max_len){
                        max_len =i-j+1;
                        max_s_start =j;
                    }
                }
                
            }
        }
        return s.substr(max_s_start,max_len);
    }
};

516 最長迴文子序列
dp[i][j]表示s[i]開頭 s[j]結尾的子串
從而通過判斷s[i]與s[j]是否相等,找到dp[i][j] 與dp[i+1][j-1]關係或與dp[j+1][i],dp[j][i-1]關係

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        if(s.size()<=1) return s.size();        
        int dp[s.size()][s.size()];        
         for(int i=0;i<s.size();++i){
            for(int j=i;j>=0;--j){
                if(i==j){
                    dp[j][i]=1;
                }else{
                    //兩端字符相同,從裏側子串推導,注意j+1==i的情況
                    if(s[i]==s[j]){
                        if(j+1<=i-1) dp[j][i]=dp[j+1][i-1]+2;
                        else{
                            dp[j][i]=2;
                        }
                    }else{
                      //兩端字符不同,則各取一端,取最大值
                        dp[j][i]=max(dp[j+1][i],dp[j][i-1]);
                    }
                }
            }
        }
        return dp[0][s.size()-1];        
    }
};

312 打氣球
此題的解題關鍵是每個氣球得分是與向鄰兩側氣球分數有關。所以可以假設氣球i爲最後一個射破氣球。再將氣球i分割爲左右兩個子區間L,R。因此依賴於L R的解。

class Solution {
public:
       //<1>核心要點:
    //想到把某個氣球作爲最後一個被射擊的,從而可以得到一個狀態。但是該氣球把整個序列化爲左右子區間
    //此時應意識到應該以區間作爲迭代元素,從小區間逐漸迭代到大區間,從而在以氣球劃分子區間時,可將子區間結果作爲已知量。   
    int maxCoins(vector<int>& nums) {
        
        if(nums.size()==0) return 0;
        if(nums.size()== 1)return nums[0];
        
        int N=nums.size();
        
        //兩側加1,作爲邊界
        vector<int>temp={1};
        for(auto i:nums){
            temp.push_back(i);
        }
        temp.push_back(1);
        
        //dp[i][j]代表只射擊區間[i,j]的最大score
        int dp[N+1][N+1];
        //依次掃描長度爲1 2 ...N的區間
        for(int len=1;len<=N;++len){
            //對每種區間長度,生成區間的首尾元素
            for(int start=1;start<=N;++start){
                int end = start+len-1;
                if(end>N) break;                
                //區間只有一個元素,直接射破
                if(start == end){
                    dp[start][end] = temp[start-1]*temp[start]*temp[end+1];
                }else{
                    //依次假設區間某個元素爲最後一個被射擊,先求其左右子區間
                    //最終得到當前區間最大值
                    dp[start][end]=0;
                    for(int last = start;last<=end;++last){
                        int left_sub_range_score =0;
                        int right_sub_range_score =0;
                        if(last-1>=start) left_sub_range_score=dp[start][last-1];
                        if(last+1<=end) right_sub_range_score=dp[last+1][end];                        
                        //射爆最後一個氣球時,由於左右子區間已經被射掉,因此乘以當前區間兩邊外側的第一個元素
                        int last_burst_score=temp[start-1]*temp[last]*temp[end+1];
                        dp[start][end] = max(dp[start][end],left_sub_range_score+last_burst_score+right_sub_range_score);
                    }
                }
            }
        }
        
        return dp[1][N];
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章