LeetCode2116
LeetCode.2116:判断一个括号字符串是否有效
通宵一夜之后上午边吃早饭边写的这道题。看到括号,配对类的很明显是要求前缀和了,这道题和前几天做的LeetCode.1963很像,不过那一道用双指针,感觉比这一道题简单一点。
题目描述
一个括号字符串是只由 (
和 )
组成的 非空 字符串。如果一个字符串满足下面 任意 一个条件,那么它就是有效的:
字符串为 ().
它可以表示为 AB(A 与 B 连接),其中A 和 B 都是有效括号字符串。
它可以表示为 (A) ,其中 A 是一个有效括号字符串。
给你一个括号字符串 s 和一个字符串 locked ,两者长度都为 n 。locked 是一个二进制字符串,只包含 0
和1
。对于 locked 中 每一个 下标 i :
如果 locked[i]
是 1
,你 不能 改变 s[i] 。
如果 locked[i]
是 0
,你 可以 将 s[i]
变为 (
或者 )
。
如果你可以将 s 变为有效括号字符串,请你返回 true ,否则返回 false 。
示例
示例1:
1 | 输入:s = "))()))", locked = "010100" |
示例2:
1 | 输入:s = "()()", locked = "0000" |
示例3:
1 | 输入:s = ")", locked = "0" |
示例4:
1 | 输入:s = "(((())(((())", locked = "111111010111" |
提示:
1 | n == s.length == locked.length |
官方题解
拿到字符串第一件事就是把(
转换成1,把)
转换成-1,这样才能适应求前缀和的方法。然后根据 有效括号字符串 的定义,其实就是括号能配上对,我们可以知道一个括号字符串有效用前缀和来表示就是该字符串总的分数为0,且任意一个位置前缀和都大于等于0。
官方题解说让自行尝试证明,我反正搞不懂咋证明,我一开始看了一眼相关标签,里面明明白白写着个栈,我就在那入栈出栈捣鼓半天,也不知道是智力不够还是通宵通的反正搞不懂。
说回来,题解在这里给出了一个 有效前缀 的定义,就是说一个字符串的某个前缀字符串满足它本身以及它所有的前缀的分数均大于等于零,则称该前缀为有效前缀。相对于 有效括号字符串 来说,将定义放宽至了其本身的分数可以大于零。
然后对字符串 s
定义最大有效分数mx
和最小有效分数mn
,具体地:
mx
代表前缀s[0...i]
可以达到的最大分数。mn
代表前缀s[0...i]
可以到达的最小分数以及作为有效前缀所需的最小分数的二者的较大值。
其中 作为有效前缀所需的最小分数 的取值,由于字符串长度的奇偶一定和其最小前缀和的奇偶性相同,故而取值不是0就是1,即为(i+1)mod2
。当i=0
时,有mx = mn = 0
。
然后从左至右遍历字符串s
,维护mx
和mn
数组的当前值,我们可以知道在当前的mx
和mn
的值满足条件的情况下,其前缀一定满足条件,故而不需要去维护整个数组。
根据locked[i]
的取值,可以获得以下情况:
1 | locked[i] = 1, 此时s[i]无法更改。因此mx = mx + diff, 其中diff为s[i]的分数。同理,mn = max(mn+diff, (i+1)mod2)。 |
遍历过程中,若是出现某一下标i
有mx<mn
,则s[0...i]
无法通过变换成为有效前缀,即s
无法通过变换成为 有效括号字符串 ,此时返回false
。
遍历结束后,只需要确定s
是否可以通过变换使得分数为0即可。假定s
长度为n
,这等价于判断mn
是否为0,如果mn=0
,则s
可以通过变换成为 有效括号字符串 ,返回true
,否则返回false
。
最终代码
1 | class Solution { |