【每日算法Day 87】今天我脫單了,所以大家不用做題了!

想啥呢,我哪來的女朋友?今天是愚人節,還是給我老老實實做題吧。

題目鏈接

LeetCode 1111. 有效括號的嵌套深度[1]

題目描述

題面太長晦澀不想看,請直接跳到最後一段。

有效括號字符串 僅由 "("")" 構成,並符合下述幾個條件之一:

  • 空字符串
  • 連接,可以記作 ABAB 連接),其中 AB 都是有效括號字符串
  • 嵌套,可以記作 (A),其中 A 是有效括號字符串

類似地,我們可以定義任意有效括號字符串 s嵌套深度 depth(s)

  • s 爲空時,depth("") = 0
  • sAB 連接時,depth(A + B) = max(depth(A), depth(B)),其中 AB 都是有效括號字符串
  • s 爲嵌套情況,depth("(" + A + ")") = 1 + depth(A),其中 A 是有效括號字符串

例如:"""()()",和 "()(()())" 都是有效括號字符串,嵌套深度分別爲 012,而 ")(""(()" 都不是有效括號字符串。

給你一個有效括號字符串 seq,將其分成兩個不相交的子序列 AB,且 AB 滿足有效括號字符串的定義(注意:A.length + B.length = seq.length)。

現在,你需要從中選出 任意 一組有效括號字符串 AB,使 max(depth(A), depth(B)) 的可能取值最小。

返回長度爲 seq.length 答案數組 answer ,選擇 A 還是 B 的編碼規則是:如果 seq[i]A 的一部分,那麼 answer[i] = 0。否則,answer[i] = 1。即便有多個滿足要求的答案存在,你也只需返回 一個

不是我吹牛,我估計一大部分人看完都不知道題目什麼意思。一句話概括就是,給你一個合法的括號序列,你需要將其拆分成兩個合法的子序列(不連續),使得兩個子序列的括號嵌套深度較大者儘量的小。

示例1

        輸入:
seq = "(()())"
輸出:
[0,1,1,1,1,0]
解釋:
拆成 "()" 和 "()()" ,最大嵌套深度爲 1

      

示例2

        輸入:
seq = "()(())()"
輸出:
[0,0,0,1,1,0,1,1]
解釋:
拆成 "()()" 和 "()()" ,最大嵌套深度爲 1

      

說明:

  • 1 <= text.size <= 10000

題解

既然想要兩個子序列的嵌套深度中較大者儘量小,那麼我們最好能夠讓兩個子序列的嵌套深度相同。

再考慮任意一個原序列中嵌套深度爲 d 的合法子序列,我們要想辦法把它拆成兩半。那麼最優的方法肯定是一半嵌套深度爲 \lfloor d/2 \rfloor ,一半是 d - \lfloor d/2 \rfloor 。這樣兩個子序列中嵌套深度較大值就是 \lceil d/2 \rceil,而其它任何分法都會導致較大值大於它。

那麼怎麼樣才能對半分呢?這個其實隨意了,但是最爲方便的方法就是,嵌套深度爲奇數的作爲一個子序列,偶數的作爲另一個子序列,這樣就對半分了,代碼還好寫。

具體實現上,我們用一個變量 cnt 來表示當前括號的嵌套深度,那麼遇到左括號就深度加一,遇到右括號嵌套深度就是當前的 cnt ,但是遍歷完這個括號之後,深度要減一,然後嵌套深度爲奇數的括號位置處標記爲 1 就行了。

僞代碼也就是:

        if c = '('
    cnt := cnt + 1
    mask := cnt&1
else
    mask := cnt&1
    cnt := cnt - 1

      

簡化

其實我們可以注意到,不管是加一還是減一,奇偶性的變化都是一致的,也就是減一之後的奇偶性和加一之後是相同的。

所以我們把減一也變成加一,那麼不管遇到什麼括號,都是 cnt 加一了,那不就變成了下標 i 了嗎?

我們把上面的僞代碼按照這種思路改變一下:

        if c = '('
    cnt := cnt + 1
    mask := cnt&1
else
    mask := cnt&1
    cnt := cnt + 1

      

然後用下標 i 替換掉 cnt :

        if c = '('
    mask := (i+1)&1
else
    mask := i&1

      

繼續改寫一下,讓形式統一一點:

        if c = '('
    mask := ~(i&1)
else
    mask := i&1

      

那麼最後就可以把這兩種情況合併了,也就是標記值直接就等於 (i&1)^(c='(')

當然我是從代碼的角度,從奇偶性推過來的,官方題解是直接嚴格證明了正確性:

官方題解:LeetCode 1111. 有效括號的嵌套深度[2]

v2-42ed7dae944c0168e1ea1589a133f753_b.jpg

代碼

c++

        class Solution {
public:
    vector<int> maxDepthAfterSplit(string seq) {
        int cnt = 0;
        vector<int> res;
        for (auto c : seq) {
            if (c == '(') {
                res.push_back((++cnt)&1);
            } else {
                res.push_back((cnt--)&1);
            }
        }
        return res;
    }
};

      

python

        class Solution:
    def maxDepthAfterSplit(self, seq: str) -> List[int]:
        cnt = 0
        res = []
        for c in seq:
            if c == '(':
                cnt += 1
                res.append(cnt&1)
            else:
                res.append(cnt&1)
                cnt -= 1
        return res

      

簡化(c++)

        class Solution {
public:
    vector<int> maxDepthAfterSplit(string seq) {
        vector<int> res;
        for (int i = 0, sz = seq.size(); i < sz; ++i) {
            res.push_back((i&1)^(seq[i]=='('));
        }
        return res;
    }
};

      

簡化(python)

        class Solution:
    def maxDepthAfterSplit(self, seq: str) -> List[int]:
        res = []
        for i, c in enumerate(seq):
            res.append((i&1)^(c=='('))
        return res

      

參考資料

[1]

LeetCode 1111. 有效括號的嵌套深度: leetcode-cn.com/problem

[2]

官方題解:LeetCode 1111. 有效括號的嵌套深度: leetcode-cn.com/problem

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