Skip to content

Commit d5a8b60

Browse files
Merge pull request SharingSource#493 from SharingSource/ac_oier
✨feat: Add 591
2 parents 013d316 + 59031a4 commit d5a8b60

File tree

3 files changed

+165
-0
lines changed

3 files changed

+165
-0
lines changed

Index/栈.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
| [232. 用栈实现队列](https://leetcode-cn.com/problems/implement-queue-using-stacks/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/implement-queue-using-stacks/solution/sha-shi-jun-tan-fu-za-du-ya-wo-de-suan-f-gb6d/) | 简单 | 🤩🤩🤩🤩🤩 |
88
| [341. 扁平化嵌套列表迭代器](https://leetcode-cn.com/problems/flatten-nested-list-iterator/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/flatten-nested-list-iterator/solution/yi-ti-shuang-jie-dfsdui-lie-di-gui-zhan-kvwhy/) | 中等 | 🤩🤩🤩 |
99
| [385. 迷你语法分析器](https://leetcode-cn.com/problems/mini-parser/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/mini-parser/solution/by-ac_oier-zuy6/) | 中等 | 🤩🤩🤩🤩🤩 |
10+
| [591. 标签验证器](https://leetcode-cn.com/problems/tag-validator/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/tag-validator/solution/by-ac_oier-9l8z/) | 困难 | 🤩🤩🤩🤩 |
1011
| [726. 原子的数量](https://leetcode-cn.com/problems/number-of-atoms/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-atoms/solution/gong-shui-san-xie-shi-yong-xiao-ji-qiao-l5ak4/) | 困难 | 🤩🤩🤩🤩 |
1112
| [1190. 反转每对括号间的子串](https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses/solution/gong-shui-san-xie-shi-yong-shuang-duan-d-r35q/) | 中等 | 🤩🤩🤩🤩🤩 |
1213
| [面试题 03.01. 三合一](https://leetcode-cn.com/problems/three-in-one-lcci/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/three-in-one-lcci/solution/yi-ti-shuang-jie-er-wei-shu-zu-yi-wei-sh-lih7/) | 简单 | 🤩🤩🤩 |

Index/模拟.md

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
| [541. 反转字符串 II](https://leetcode-cn.com/problems/reverse-string-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/reverse-string-ii/solution/gong-shui-san-xie-jian-dan-zi-fu-chuan-m-p88f/) | 简单 | 🤩🤩🤩🤩🤩 |
7272
| [551. 学生出勤记录 I](https://leetcode-cn.com/problems/student-attendance-record-i/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/student-attendance-record-i/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-hui7/) | 简单 | 🤩🤩🤩 |
7373
| [566. 重塑矩阵](https://leetcode-cn.com/problems/reshape-the-matrix/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/reshape-the-matrix/solution/jian-dan-ti-zhong-quan-chu-ji-ke-yi-kan-79gv5/) | 简单 | 🤩🤩🤩 |
74+
| [591. 标签验证器](https://leetcode-cn.com/problems/tag-validator/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/tag-validator/solution/by-ac_oier-9l8z/) | 困难 | 🤩🤩🤩🤩 |
7475
| [594. 最长和谐子序列](https://leetcode-cn.com/problems/longest-harmonious-subsequence/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/longest-harmonious-subsequence/solution/gong-shui-san-xie-yi-ti-shuang-jie-hua-d-quuh/) | 简单 | 🤩🤩🤩🤩 |
7576
| [598. 范围求和 II](https://leetcode-cn.com/problems/range-addition-ii/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/range-addition-ii/solution/gong-shui-san-xie-jian-dan-mo-ni-ti-by-a-006h/) | 简单 | 🤩🤩🤩 |
7677
| [599. 两个列表的最小索引总和](https://leetcode-cn.com/problems/minimum-index-sum-of-two-lists/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/minimum-index-sum-of-two-lists/solution/by-ac_oier-oh5b/) | 简单 | 🤩🤩🤩🤩 |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
### 题目描述
2+
3+
这是 LeetCode 上的 **[591. 标签验证器](https://leetcode-cn.com/problems/tag-validator/solution/by-ac_oier-9l8z/)** ,难度为 **困难**
4+
5+
Tag : 「模拟」、「栈」
6+
7+
m
8+
9+
给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:
10+
11+
1. 代码必须被合法的闭合标签包围。否则,代码是无效的。
12+
2. 闭合标签(不一定合法)要严格符合格式:`<TAG_NAME>TAG_CONTENT</TAG_NAME>`。其中,`<TAG_NAME>` 是起始标签,`</TAG_NAME>` 是结束标签。起始和结束标签中的 `TAG_NAME` 应当相同。当且仅当 `TAG_NAME``TAG_CONTENT` 都是合法的,闭合标签才是合法的。
13+
3. 合法的 `TAG_NAME` 仅含有大写字母,长度在范围 $[1,9]$ 之间。否则,该 `TAG_NAME` 是不合法的。
14+
4. 合法的 `TAG_CONTENT` 可以包含其他合法的闭合标签,`cdata` ( 请参考规则 $7$ )和任意字符( 注意参考规则 $1$ )除了不匹配的 `<`、不匹配的起始和结束标签、不匹配的或带有不合法 `TAG_NAME` 的闭合标签。否则,`TAG_CONTENT` 是不合法的。
15+
5. 一个起始标签,如果没有具有相同 `TAG_NAME` 的结束标签与之匹配,是不合法的。反之亦然。不过,你也需要考虑标签嵌套的问题。
16+
6. 一个 `<`,如果你找不到一个后续的 `>` 与之匹配,是不合法的。并且当你找到一个 `<``</` 时,所有直到下一个 `>` 的前的字符,都应当被解析为 `TAG_NAME`(不一定合法)。
17+
7. `cdata` 有如下格式:`<![CDATA[CDATA_CONTENT]]>``CDATA_CONTENT` 的范围被定义成 `<![CDATA[` 和后续的第一个 `]]>`之间的字符。
18+
8. `CDATA_CONTENT` 可以包含任意字符。`cdata` 的功能是阻止验证器解析 `CDATA_CONTENT`,所以即使其中有一些字符可以被解析为标签(无论合法还是不合法),也应该将它们视为常规字符。
19+
20+
合法代码的例子:
21+
```
22+
输入: "<DIV>This is the first line <![CDATA[<div>]]></DIV>"
23+
24+
输出: True
25+
26+
解释:
27+
28+
代码被包含在了闭合的标签内: <DIV> 和 </DIV> 。
29+
30+
TAG_NAME 是合法的,TAG_CONTENT 包含了一些字符和 cdata 。
31+
32+
即使 CDATA_CONTENT 含有不匹配的起始标签和不合法的 TAG_NAME,它应该被视为普通的文本,而不是标签。
33+
34+
所以 TAG_CONTENT 是合法的,因此代码是合法的。最终返回True。
35+
36+
37+
输入: "<DIV>>> ![cdata[]] <![CDATA[<div>]>]]>]]>>]</DIV>"
38+
39+
输出: True
40+
41+
解释:
42+
43+
我们首先将代码分割为: start_tag|tag_content|end_tag 。
44+
45+
start_tag -> "<DIV>"
46+
47+
end_tag -> "</DIV>"
48+
49+
tag_content 也可被分割为: text1|cdata|text2 。
50+
51+
text1 -> ">> ![cdata[]] "
52+
53+
cdata -> "<![CDATA[<div>]>]]>" ,其中 CDATA_CONTENT 为 "<div>]>"
54+
55+
text2 -> "]]>>]"
56+
57+
58+
start_tag 不是 "<DIV>>>" 的原因参照规则 6 。
59+
cdata 不是 "<![CDATA[<div>]>]]>]]>" 的原因参照规则 7 。
60+
```
61+
不合法代码的例子:
62+
```
63+
输入: "<A> <B> </A> </B>"
64+
输出: False
65+
解释: 不合法。如果 "<A>" 是闭合的,那么 "<B>" 一定是不匹配的,反之亦然。
66+
67+
输入: "<DIV> div tag is not closed <DIV>"
68+
输出: False
69+
70+
输入: "<DIV> unmatched < </DIV>"
71+
输出: False
72+
73+
输入: "<DIV> closed tags with invalid tag name <b>123</b> </DIV>"
74+
输出: False
75+
76+
输入: "<DIV> unmatched tags with invalid tag name </1234567890> and <CDATA[[]]> </DIV>"
77+
输出: False
78+
79+
输入: "<DIV> unmatched start tag <B> and unmatched end tag </C> </DIV>"
80+
输出: False
81+
```
82+
注意:
83+
* 为简明起见,你可以假设输入的代码(包括提到的任意字符)只包含`数字`, `字母`, `'<'`,`'>'`,`'/'`,`'!'`,`'['`,`']'``' '`
84+
85+
---
86+
87+
### 模拟(栈)
88+
89+
字符串大模拟,假设字符串 `s` 长度为 $n$,当前处理到的位置为 $i$,根据以下优先级进行检查:
90+
91+
1. 优先尝试检查以 $i$ 为开始的连续段是否为 `CDATA`,若能匹配到开头,则尝试匹配到 `CDATA` 的结尾处,并更新 $i$,若无法找到结尾,返回 `False`
92+
2. 尝试匹配 $s[i]$ 是否为 `<`,若满足,则根据 $s[i + 1]$ 是否为 `/` 来判断当前 `TAG_NAME` 是处于右边还是左边,然后将 `TAG_NAME` 取出,记为 $tag$,判断 $tag$ 长度是否合法,不合法返回 `False`,合法则根据是左边还是右边的 `TAG_NAME` 分情况讨论:
93+
* 位于左边的 `TAG_NAME`:将其加入栈中,等待右边的 `TAG_NAME` 与其匹配;
94+
* 位于右边的 `TAG_NAME`:将其与当前栈顶的元素进行匹配,若栈为空或匹配不上,返回 `False`(注意:由于整个 `s` 应当被一对 `TAG_NAME` 所包裹,因此如果取出后栈顶元素匹配后,栈为空,同时又还没处理完整个 `s`,此时 `s` 也不合法,返回 `Fasle`);
95+
3. 其余情况则为普通字符。
96+
97+
最后由于整个 `s` 应当被一对 `TAG_NAME` 所包裹,因此当 $i = 0$ 时,不能是情况 $1$ 和情况 $3$,需要特判一下。
98+
99+
代码:
100+
```Java
101+
class Solution {
102+
String CDATA1 = "<![CDATA[", CDATA2 = "]]>";
103+
public boolean isValid(String s) {
104+
Deque<String> d = new ArrayDeque<>();
105+
int n = s.length(), i = 0;
106+
while (i < n) {
107+
if (i + 8 < n && s.substring(i, i + 9).equals(CDATA1)) {
108+
if (i == 0) return false;
109+
int j = i + 9;
110+
boolean ok = false;
111+
while (j < n && !ok) {
112+
if (j + 2 < n && s.substring(j, j + 3).equals(CDATA2)) {
113+
j = j + 3; ok = true;
114+
} else {
115+
j++;
116+
}
117+
}
118+
if (!ok) return false;
119+
i = j;
120+
} else if (s.charAt(i) == '<') {
121+
if (i == n - 1) return false;
122+
boolean isEnd = s.charAt(i + 1) == '/';
123+
int p = isEnd ? i + 2 : i + 1, j = p;
124+
while (j < n && s.charAt(j) != '>') {
125+
if (!Character.isUpperCase(s.charAt(j))) return false;
126+
j++;
127+
}
128+
if (j == n) return false;
129+
int len = j - p;
130+
if (len < 1 || len > 9) return false;
131+
String tag = s.substring(p, j);
132+
i = j + 1;
133+
if (!isEnd) {
134+
d.addLast(tag);
135+
} else {
136+
if (d.isEmpty() || !d.pollLast().equals(tag)) return false;
137+
if (d.isEmpty() && i < n) return false;
138+
}
139+
140+
} else {
141+
if (i == 0) return false;
142+
i++;
143+
}
144+
}
145+
return d.isEmpty();
146+
}
147+
}
148+
```
149+
* 时间复杂度:$O(n)$
150+
* 空间复杂度:$O(n)$
151+
152+
---
153+
154+
### 最后
155+
156+
这是我们「刷穿 LeetCode」系列文章的第 `No.591` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
157+
158+
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
159+
160+
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode
161+
162+
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
163+

0 commit comments

Comments
 (0)