32. 最長有效括號(hào)

一丙号、題目

給定一個(gè)只包含 '(' 和 ')' 的字符串,找出最長的包含有效括號(hào)的子串的長度牲证。

示例 1:

輸入: "(()"
輸出: 2
解釋: 最長有效括號(hào)子串為 "()"
示例 2:

輸入: ")()())"
輸出: 4
解釋: 最長有效括號(hào)子串為 "()()"

二巨双、解答

2.1方法一:動(dòng)態(tài)規(guī)劃
2.1.1 思路
2.1.1.1 定義動(dòng)態(tài)規(guī)劃數(shù)組

dp [ i ] 代表以下標(biāo) i 結(jié)尾的合法序列的最長長度,例如下圖

下標(biāo) 1 結(jié)尾的最長合法字符串長度是 2熔恢,下標(biāo) 3 結(jié)尾的最長字符串是 str [ 0 , 3 ]脐湾,長度是 4 臭笆。

2.1.1.2 規(guī)律
  • 首先我們初始化所有的 dp 都等于零。
  • 以左括號(hào)結(jié)尾的字符串一定是非法序列秤掌,所以 dp 是零愁铺,不用更改。
  • 以右括號(hào)結(jié)尾的字符串分兩種情況闻鉴。

a.右括號(hào)前邊是 ( 茵乱,類似于 ……()。

dp [ i ] = dp [ i - 2] + 2 (前一個(gè)合法序列的長度孟岛,加上當(dāng)前新增的長度 2)

類似于上圖中 index = 3 的時(shí)候的情況瓶竭。

dp [ 3 ] = dp [ 3 - 2 ] + 2 = dp [ 1 ] + 2 = 2 + 2 = 4

b.右括號(hào)前邊是 ),類似于 ……))渠羞。

此時(shí)我們需要判斷 i - dp[i - 1] - 1 (前一個(gè)合法序列的前邊一個(gè)位置) 是不是左括號(hào)斤贰。

例如上圖的 index = 7 的時(shí)候,此時(shí) index - 1 也是右括號(hào)次询,我們需要知道 i - dp[i - 1] - 1 = 7 - dp [ 6 ] - 1 = 4 位置的括號(hào)的情況荧恍。

而剛好 index = 4 的位置是左括號(hào),此時(shí) dp [ i ] = dp [ i - 1 ] + dp [ i - dp [ i - 1] - 2 ] + 2 (當(dāng)前位置的前一個(gè)合法序列的長度屯吊,加上匹配的左括號(hào)前邊的合法序列的長度送巡,加上新增的長度 2),也就是 dp [ 7 ] = dp [ 7 - 1 ] + dp [ 7 - dp [ 7 - 1] - 2 ] + 2 = dp [ 6 ] + dp [7 - 2 - 2] + 2 = 2 + 4 + 2 = 8盒卸。

如果 index = 4 不是左括號(hào)骗爆,那么此時(shí)位置 7 的右括號(hào)沒有匹配的左括號(hào),所以 dp [ 7 ] = 0 蔽介,不需要更新摘投。

2.1.1 實(shí)現(xiàn)
public int longestValidParentheses(String s) {
    int maxans = 0;
    int dp[] = new int[s.length()];
    for (int i = 1; i < s.length(); i++) {
        if (s.charAt(i) == ')') {
            //右括號(hào)前邊是左括號(hào)
            if (s.charAt(i - 1) == '(') {
                dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
            //右括號(hào)前邊是右括號(hào)糟需,并且除去前邊的合法序列的前邊是左括號(hào)
            } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
            }
            maxans = Math.max(maxans, dp[i]);
        }
    }
    return maxans;
}

時(shí)間復(fù)雜度:遍歷了一次,O(n)谷朝。

空間復(fù)雜度:O(n)洲押。

2.2方法二:棧
2.2.1 思路

從左到右掃描字符串,棧頂保存當(dāng)前掃描的時(shí)候圆凰,合法序列前的一個(gè)位置位置下標(biāo)是多少杈帐,啥意思嘞?

我們掃描到左括號(hào)专钉,就將當(dāng)前位置入棧挑童。

掃描到右括號(hào),就將棧頂出棧(代表?xiàng)m數(shù)淖罄ㄌ?hào)匹配到了右括號(hào))跃须,然后分兩種情況站叼。

  • 棧不空,那么就用當(dāng)前的位置減去棧頂?shù)拇娴奈恢霉矫瘢缓缶偷玫疆?dāng)前合法序列的長度尽楔,然后更新一下最長長度。

  • 棧是空的第练,說明之前沒有與之匹配的左括號(hào)阔馋,那么就將當(dāng)前的位置入棧。

看下圖示娇掏,更好的理解一下呕寝。


step1
step2
step3
step4
step5
step6
step7
2.2.2 實(shí)現(xiàn)
public int longestValidParentheses(String s) {
    int maxans = 0;
    Stack<Integer> stack = new Stack<>();
    stack.push(-1);
    for (int i = 0; i < s.length(); i++) {
        if (s.charAt(i) == '(') {
            stack.push(i);
        } else {
            stack.pop();
            if (stack.empty()) {
                stack.push(i);
            } else {
                maxans = Math.max(maxans, i - stack.peek());
            }
        }
    }
    return maxans;
}

時(shí)間復(fù)雜度: O(n)。

空間復(fù)雜度:O(n)婴梧。

2.3方法三:大神操作
2.3.1 思路

保持時(shí)間復(fù)雜度是 O(n)下梢,將空間復(fù)雜度優(yōu)化到了 O(1),它的動(dòng)機(jī)是怎么想到的沒有理出來塞蹭,就介紹下它的想法吧孽江。

從左到右掃描,用兩個(gè)變量 left 和 right 保存的當(dāng)前的左括號(hào)和右括號(hào)的個(gè)數(shù)浮还,都初始化為 0 竟坛。

如果左括號(hào)個(gè)數(shù)等于右括號(hào)個(gè)數(shù)了,那么就更新合法序列的最長長度钧舌。
如果左括號(hào)個(gè)數(shù)大于右括號(hào)個(gè)數(shù)了担汤,那么就接著向右邊掃描。
如果左括號(hào)數(shù)目小于右括號(hào)個(gè)數(shù)了洼冻,那么后邊無論是什么崭歧,此時(shí)都不可能是合法序列了,此時(shí) left 和 right 歸 0撞牢,然后接著掃描率碾。
從左到右掃描完畢后叔营,同樣的方法從右到左再來一次,因?yàn)轭愃七@樣的情況 ( ( ( ) ) 所宰,如果從左到右掃描到最后绒尊,left = 3,right = 2仔粥,期間不會(huì)出現(xiàn) left == right婴谱。但是如果從右向左掃描,掃描到倒數(shù)第二個(gè)位置的時(shí)候躯泰,就會(huì)出現(xiàn) left = 2谭羔,right = 2 ,就會(huì)得到一種合法序列麦向。

2.3.2 實(shí)現(xiàn)
public static 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;
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘟裸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诵竭,更是在濱河造成了極大的恐慌话告,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秀撇,死亡現(xiàn)場(chǎng)離奇詭異超棺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)呵燕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來件相,“玉大人再扭,你說我怎么就攤上這事∫勾#” “怎么了泛范?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長紊撕。 經(jīng)常有香客問我罢荡,道長,這世上最難降的妖魔是什么对扶? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任区赵,我火速辦了婚禮,結(jié)果婚禮上浪南,老公的妹妹穿的比我還像新娘笼才。我一直安慰自己,他們只是感情好络凿,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布骡送。 她就那樣靜靜地躺著昂羡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摔踱。 梳的紋絲不亂的頭發(fā)上虐先,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音派敷,去河邊找鬼赴穗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛膀息,可吹牛的內(nèi)容都是我干的般眉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼潜支,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼甸赃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起冗酿,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤埠对,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后裁替,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體项玛,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年弱判,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了襟沮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡昌腰,死狀恐怖开伏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情遭商,我是刑警寧澤固灵,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站劫流,受9級(jí)特大地震影響巫玻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜祠汇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一仍秤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧座哩,春花似錦徒扶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽导坟。三九已至,卻和暖如春圈澈,著一層夾襖步出監(jiān)牢的瞬間惫周,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國打工康栈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留递递,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓啥么,卻偏偏與公主長得像登舞,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悬荣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容

  • 題目給定一個(gè)只包含 '(' 和 ')' 的字符串菠秒,找出最長的包含有效括號(hào)的子串的長度。 示例 1:輸入: "(()...
    HITZGD閱讀 712評(píng)論 0 0
  • 題目鏈接難度:困難 類型: 字符串氯迂、動(dòng)態(tài)規(guī)劃 給定一個(gè)只包含 '(' 和 ')' 的字符串践叠,找...
    wzNote閱讀 924評(píng)論 0 1
  • 一、題目 給定一個(gè)只包含 '(' 和 ')' 的字符串嚼蚀,找出最長的包含有效括號(hào)的子串的長度禁灼。 示例 1: 示例 2...
    Mage閱讀 665評(píng)論 0 0
  • 今天的任務(wù) 提升思考維度 身份、種類 轿曙、特點(diǎn)從三個(gè)緯度了解鯨
    Grace沈俊岳閱讀 88評(píng)論 0 0
  • 憲問第十四(主要記錄孔子和其弟子論修身為人之道弄捕,以及對(duì)古人的評(píng)價(jià)) 每日《論語》編輯:曹友寶 【原文】 14.5南...
    曹友寶閱讀 328評(píng)論 0 0