動機

以後遇到回文優先考慮中心展開

Problem

Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.

 

Example 1:

Input: s = (()Output: 2Explanation: The longest valid parentheses substring is ().

Example 2:

Input: s = )()())Output: 4Explanation: The longest valid parentheses substring is ()().

Example 3:

Input: s = Output: 0

 

Constraints:

  • 0 <= s.length <= 3 * 104
  • s[i] is '(', or ')'.

Ver1: TLE

int dp[2000][2000] = {0};
class Solution {
public:
    int getEval(int i,int j) { return !dp[i][j] ? -1 : dp[i][j]; }
    bool notEval(int i,int j) { return dp[i][j] == 0;}
    int f(string &s,int i,int j) {
        if(i > s.size() || j < 0)
            return -1;
        else if(i >= j)
            dp[i][j] = -1;
        if(notEval(i,j)) {
            if(s[i] == '(' && s[j] == ')') { // !!
                for(int k=i+1;k+1<j;k++)
                    if(s[k] == ')' && s[k+1] == '(') // !!
                        if(f(s,i,k) > 0 && f(s,k+1,j) > 0)
                            dp[i][j] = max(getEval(i,j),f(s,i,k)+f(s,k+1,j));
                dp[i][j] = max(getEval(i,j), f(s,i+1,j-1) > 0 ? f(s,i+1,j-1)+2 : -1);
            }
            else
                dp[i][j] = -1;
        }
        return getEval(i,j);
    }
    void initDP(string &s) {
        memset(dp, 0, sizeof(dp));
        for(int i=0;i<s.size();i++)
            dp[i][i] = -1;
    }
    void find_out_single_pair(string &s) { // 找()
        for(int i=0;i+1<s.size();i++)
            if(s[i] == '(' && s[i+1]==')')
                dp[i][i+1] = 2;
    }
    int longestValidParentheses(string s) {
        initDP(s);
        find_out_single_pair(s);
        int ans = 0;
        for(int i=0;i<s.size();i++)
            for(int j=0;j<s.size();j++)
                ans = max(ans,f(s,i,j));
        //ans = f(s,0,s.size()-1);
        //for(int i=0;i<s.size();i++) {
        //    for(int j=0;j<s.size();j++)
        //        cout << dp[i][j] << ' ';
        //    cout << '\n';
        //}
        return ans;
    }
};

Ver2: TLE

等等,為什麼每次run test拋出來的時間都不一樣啊 一下76ms,一下超時

int dp[2000][2000] = {0};
class Solution {
public:
    int ans = 0;
    void initDP(string &s) {
        memset(dp, 0, sizeof(dp));
        for(int i=0;i<s.size();i++)
            dp[i][i] = -1;
        ans = 0;
    }
    void setDP(int i,int j,int val) {
        dp[i][j] = val, ans = max(ans,val);
    }
    
    bool isPair(string &s,int i,int j) { return s[i] == '(' && s[j] == ')'; }
    void compDiv(string &s,int i,int j) {
        for(int x=i+1;x+1<j;x++) // [i~k] + [k+1~j]
            if(isPair(s,x+1,x) && dp[i][x] > 0 && dp[x+1][j] > 0)
                setDP(i,j,dp[i][x] + dp[x+1][j]);
    }
    int longestValidParentheses(string s) {
        initDP(s);
        for(int i=0;i+1<s.size();i++)
            if(isPair(s,i,i+1)) {
                setDP(i,i+1,2);
                for(int k=1;i-k >= 0 && (i+1)+k < s.size() && isPair(s,i-k,(i+1)+k);k++)
                    setDP(i-k,(i+1)+k,dp[i-(k-1)][(i+1)+(k-1)] + 2);
            }
        for(int k=1;k<s.size();k++)
            for(int j=k,i=0;j<s.size();j++,i++)
                if(isPair(s,i,j)) {
                    compDiv(s,i,j);
                    if(i+1 < s.size() && j-1 >= 0 && dp[i+1][j-1] > 0)
                        setDP(i,j,max(dp[i][j], dp[i+1][j-1] + 2));
                }       
        /*
        for(int i=0;i<s.size();i++) {
            for(int j=0;j<s.size();j++)
                cout << dp[i][j] << ' ';
            cout << '\n';
        }
        */
        return ans;
    }
};

Ver3: AC

定義: dp[i]是到i為止最長的括號長度

int dp[100000] = {0};
int ans = 0,len=0;
class Solution {
public:
    void initDP(string &s) {
        memset(dp, 0, sizeof(dp));
        ans = 0;
        len=s.size();
    }
    bool isLegal(int i) { return i >= 0 && i < len; }
    bool isPair(string &s,int i,int j) { return isLegal(i) && isLegal(j) && s[i] == '(' && s[j] == ')'; }
    bool isCons(string &s,int i,int j) { return isLegal(i) && isLegal(j) && s[i] == ')' && s[j] == ')'; }
    int getDP(int i) {
        return isLegal(i) ? dp[i] : 0;
    }
    void setDP(int i,int val) {
        dp[i] = val, ans = max(ans,val);
    }
    int longestValidParentheses(string s) {
        initDP(s);
        for(int i=1;i<s.size();i++)
            if(isPair(s,i-1,i))
                setDP(i,getDP(i-2)+2);
            else if(isCons(s,i-1,i) && getDP(i-1) > 0 && isPair(s,i-getDP(i-1)-1,i))
                setDP(i,getDP(i-1)+2+getDP(i-2-getDP(i-1)));
        return ans;
    }
};

case study

從左邊往右看,左是開頭,只有對應的右就會被算進長度,但一旦右超過,左就再也不能被算,就要重新計數

所以可以左右兩邊都算,找最大的

public class Solution {
    public int longestValidParentheses(String s) {
        int left = 0, right = 0, maxlength = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * right);
            } else if (right >= left) {
                left = right = 0;
            }
        }
        left = right = 0;
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * left);
            } else if (left >= right) {
                left = right = 0;
            }
        }
        return maxlength;
    }
}