Skip to content

Commit 58cfdf1

Browse files
committed
+ problem 591
1 parent e03ae7b commit 58cfdf1

File tree

5 files changed

+251
-0
lines changed

5 files changed

+251
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# 591. Tag Validator
2+
Given a string representing a code snippet, implement a tag validator to parse the code and return whether it is valid.
3+
4+
A code snippet is valid if all the following rules hold:
5+
1. The code must be wrapped in a **valid closed tag**. Otherwise, the code is invalid.
6+
2. A **closed tag** (not necessarily valid) has exactly the following format : `<TAG_NAME>TAG_CONTENT</TAG_NAME>`. Among them, `<TAG_NAME>` is the start tag, and `</TAG_NAME>` is the end tag. The TAG_NAME in start and end tags should be the same. A closed tag is **valid** if and only if the TAG_NAME and TAG_CONTENT are valid.
7+
3. A **valid** `TAG_NAME` only contain **upper-case letters**, and has length in range [1,9]. Otherwise, the `TAG_NAME` is **invalid**.
8+
4. A **valid** `TAG_CONTENT` may contain other **valid closed tags**, **cdata** and any characters (see note1) **EXCEPT** unmatched `<`, unmatched start and end tag, and unmatched or closed tags with invalid TAG_NAME. Otherwise, the `TAG_CONTENT` is **invalid**.
9+
5. A start tag is unmatched if no end tag exists with the same TAG_NAME, and vice versa. However, you also need to consider the issue of unbalanced when tags are nested.
10+
6. A `<` is unmatched if you cannot find a subsequent `>`. And when you find a `<` or `</`, all the subsequent characters until the next `>` should be parsed as TAG_NAME (not necessarily valid).
11+
7. The cdata has the following format : `<![CDATA[CDATA_CONTENT]]>`. The range of `CDATA_CONTENT` is defined as the characters between `<![CDATA[` and the **first subsequent** `]]>`.
12+
8. `CDATA_CONTENT` may contain **any characters**. The function of cdata is to forbid the validator to parse `CDATA_CONTENT`, so even it has some characters that can be parsed as tag (no matter valid or invalid), you should treat it as **regular characters**.
13+
14+
#### Example 1:
15+
<pre>
16+
<strong>Input:</strong> code = "<DIV>This is the first line <![CDATA[<div>]]></DIV>"
17+
<strong>Output:</strong> true
18+
<strong>Explanation:</strong>
19+
The code is wrapped in a closed tag : <DIV> and </DIV>.
20+
The TAG_NAME is valid, the TAG_CONTENT consists of some characters and cdata.
21+
Although CDATA_CONTENT has an unmatched start tag with invalid TAG_NAME, it should be considered as plain text, not parsed as a tag.
22+
So TAG_CONTENT is valid, and then the code is valid. Thus return true.
23+
</pre>
24+
25+
#### Example 2:
26+
<pre>
27+
<strong>Input:</strong> code = "<DIV>>> ![cdata[]] <![CDATA[<div>]>]]>]]>>]</DIV>"
28+
<strong>Output:</strong> true
29+
<strong>Explanation:</strong>
30+
We first separate the code into : start_tag|tag_content|end_tag.
31+
start_tag -> "<DIV>"
32+
end_tag -> "</DIV>"
33+
tag_content could also be separated into : text1|cdata|text2.
34+
text1 -> ">> ![cdata[]] "
35+
cdata -> "<![CDATA[<div>]>]]>", where the CDATA_CONTENT is "<div>]>"
36+
text2 -> "]]>>]"
37+
The reason why start_tag is NOT "<DIV>>>" is because of the rule 6.
38+
The reason why cdata is NOT "<![CDATA[<div>]>]]>]]>" is because of the rule 7.
39+
</pre>
40+
41+
#### Example 3:
42+
<pre>
43+
<strong>Input:</strong> code = "<A> <B> </A> </B>"
44+
<strong>Output:</strong> false
45+
<strong>Explanation:</strong> Unbalanced. If "<A>" is closed, then "<B>" must be unmatched, and vice versa.
46+
</pre>
47+
48+
#### Constraints:
49+
* `1 <= code.length <= 500`
50+
* `code` consists of English letters, digits, `'<'`, `'>'`, `'/'`, `'!'`, `'['`, `']'`, `'.'`, and `' '`.
51+
52+
## Solutions (Python)
53+
54+
### 1. Solution
55+
```Python
56+
class Solution:
57+
def isValid(self, code: str) -> bool:
58+
cdata = False
59+
tagstack = []
60+
i = 0
61+
62+
while i < len(code):
63+
if cdata:
64+
if code[i:i + 3] == "]]>":
65+
cdata = False
66+
i += 2
67+
elif tagstack == [] and (code[i] != '<' or code[i:i + 2] in "</<!"):
68+
return False
69+
elif code[i:i + 9] == "<![CDATA[":
70+
cdata = True
71+
i += 8
72+
elif code[i:i + 2] == "</":
73+
for j in range(i + 2, i + 13):
74+
if j >= len(code) or j == i + 12 or (j == i + 2 and code[j] == '>'):
75+
return False
76+
elif code[j] == '>':
77+
if tagstack.pop() != code[i + 2:j]:
78+
return False
79+
if tagstack == [] and j != len(code) - 1:
80+
return False
81+
i = j
82+
break
83+
elif not code[j].isupper():
84+
return False
85+
elif code[i] == '<':
86+
for j in range(i + 1, i + 12):
87+
if j >= len(code) or j == i + 11 or (j == i + 1 and code[j] == '>'):
88+
return False
89+
elif code[j] == '>':
90+
tagstack.append(code[i + 1:j])
91+
i = j
92+
break
93+
elif not code[j].isupper():
94+
return False
95+
96+
i += 1
97+
98+
return tagstack == []
99+
```
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# 591. 标签验证器
2+
给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:
3+
1. 代码必须被**合法的闭合标签**包围。否则,代码是无效的。
4+
2. **闭合标签**(不一定合法)要严格符合格式:`<TAG_NAME>TAG_CONTENT</TAG_NAME>`。其中,`<TAG_NAME>`是起始标签,`</TAG_NAME>`是结束标签。起始和结束标签中的 TAG_NAME 应当相同。当且仅当 TAG_NAME 和 TAG_CONTENT 都是合法的,闭合标签才是**合法的**
5+
3. **合法的** `TAG_NAME` 仅含有**大写字母**,长度在范围 [1,9] 之间。否则,该 `TAG_NAME`**不合法的**
6+
4. **合法的** `TAG_CONTENT` 可以包含其他**合法的闭合标签****cdata** (请参考规则7)和任意字符(注意参考规则1)除了不匹配的`<`、不匹配的起始和结束标签、不匹配的或带有不合法 TAG_NAME 的闭合标签。否则,`TAG_CONTENT`**不合法的**
7+
5. 一个起始标签,如果没有具有相同 TAG_NAME 的结束标签与之匹配,是不合法的。反之亦然。不过,你也需要考虑标签嵌套的问题。
8+
6. 一个`<`,如果你找不到一个后续的`>`与之匹配,是不合法的。并且当你找到一个`<``</`时,所有直到下一个`>`的前的字符,都应当被解析为 TAG_NAME(不一定合法)。
9+
7. cdata 有如下格式:`<![CDATA[CDATA_CONTENT]]>``CDATA_CONTENT` 的范围被定义成 `<![CDATA[`**后续的第一个** `]]>`之间的字符。
10+
8. `CDATA_CONTENT` 可以包含**任意字符**。cdata 的功能是阻止验证器解析`CDATA_CONTENT`,所以即使其中有一些字符可以被解析为标签(无论合法还是不合法),也应该将它们视为**常规字符**
11+
12+
#### 合法代码的例子:
13+
<pre>
14+
<strong>输入:</strong> "<DIV>This is the first line <![CDATA[<div>]]></DIV>"
15+
<strong>输出:</strong> True
16+
<strong>解释:</strong>
17+
代码被包含在了闭合的标签内: <DIV> 和 </DIV> 。
18+
TAG_NAME 是合法的,TAG_CONTENT 包含了一些字符和 cdata 。
19+
即使 CDATA_CONTENT 含有不匹配的起始标签和不合法的 TAG_NAME,它应该被视为普通的文本,而不是标签。
20+
所以 TAG_CONTENT 是合法的,因此代码是合法的。最终返回True。
21+
22+
<strong>输入:</strong> "<DIV>>> ![cdata[]] <![CDATA[<div>]>]]>]]>>]</DIV>"
23+
<strong>输出:</strong> True
24+
<strong>解释:</strong>
25+
我们首先将代码分割为: start_tag|tag_content|end_tag 。
26+
start_tag -> "<DIV>"
27+
end_tag -> "</DIV>"
28+
tag_content 也可被分割为: text1|cdata|text2 。
29+
text1 -> ">> ![cdata[]] "
30+
cdata -> "<![CDATA[<div>]>]]>" ,其中 CDATA_CONTENT 为 "<div>]>"
31+
text2 -> "]]>>]"
32+
start_tag 不是 "<DIV>>>" 的原因参照规则 6 。
33+
cdata 不是 "<![CDATA[<div>]>]]>]]>" 的原因参照规则 7 。
34+
</pre>
35+
36+
#### 不合法代码的例子:
37+
<pre>
38+
<strong>输入:</strong> "<A> <B> </A> </B>"
39+
<strong>输出:</strong> False
40+
<strong>解释:</strong> 不合法。如果 "<A>" 是闭合的,那么 "<B>" 一定是不匹配的,反之亦然。
41+
<strong>输入:</strong> "<DIV> div tag is not closed <DIV>"
42+
<strong>输出:</strong> False
43+
<strong>输入:</strong> "<DIV> unmatched < </DIV>"
44+
<strong>输出:</strong> False
45+
<strong>输入:</strong> "<DIV> closed tags with invalid tag name <b>123</b> </DIV>"
46+
<strong>输出:</strong> False
47+
<strong>输入:</strong> "<DIV> unmatched tags with invalid tag name </1234567890> and <CDATA[[]]> </DIV>"
48+
<strong>输出:</strong> False
49+
<strong>输入:</strong> "<DIV> unmatched start tag <B> and unmatched end tag </C> </DIV>"
50+
<strong>输出:</strong> False
51+
</pre>
52+
53+
#### 注意:
54+
1. 为简明起见,你可以假设输入的代码(包括提到的**任意字符**)只包含`数字`, `字母`, `'<'`,`'>'`,`'/'`,`'!'`,`'['`,`']'``' '`
55+
56+
## 题解 (Python)
57+
58+
### 1. 题解
59+
```Python
60+
class Solution:
61+
def isValid(self, code: str) -> bool:
62+
cdata = False
63+
tagstack = []
64+
i = 0
65+
66+
while i < len(code):
67+
if cdata:
68+
if code[i:i + 3] == "]]>":
69+
cdata = False
70+
i += 2
71+
elif tagstack == [] and (code[i] != '<' or code[i:i + 2] in "</<!"):
72+
return False
73+
elif code[i:i + 9] == "<![CDATA[":
74+
cdata = True
75+
i += 8
76+
elif code[i:i + 2] == "</":
77+
for j in range(i + 2, i + 13):
78+
if j >= len(code) or j == i + 12 or (j == i + 2 and code[j] == '>'):
79+
return False
80+
elif code[j] == '>':
81+
if tagstack.pop() != code[i + 2:j]:
82+
return False
83+
if tagstack == [] and j != len(code) - 1:
84+
return False
85+
i = j
86+
break
87+
elif not code[j].isupper():
88+
return False
89+
elif code[i] == '<':
90+
for j in range(i + 1, i + 12):
91+
if j >= len(code) or j == i + 11 or (j == i + 1 and code[j] == '>'):
92+
return False
93+
elif code[j] == '>':
94+
tagstack.append(code[i + 1:j])
95+
i = j
96+
break
97+
elif not code[j].isupper():
98+
return False
99+
100+
i += 1
101+
102+
return tagstack == []
103+
```
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
class Solution:
2+
def isValid(self, code: str) -> bool:
3+
cdata = False
4+
tagstack = []
5+
i = 0
6+
7+
while i < len(code):
8+
if cdata:
9+
if code[i:i + 3] == "]]>":
10+
cdata = False
11+
i += 2
12+
elif tagstack == [] and (code[i] != '<' or code[i:i + 2] in "</<!"):
13+
return False
14+
elif code[i:i + 9] == "<![CDATA[":
15+
cdata = True
16+
i += 8
17+
elif code[i:i + 2] == "</":
18+
for j in range(i + 2, i + 13):
19+
if j >= len(code) or j == i + 12 or (j == i + 2 and code[j] == '>'):
20+
return False
21+
elif code[j] == '>':
22+
if tagstack.pop() != code[i + 2:j]:
23+
return False
24+
if tagstack == [] and j != len(code) - 1:
25+
return False
26+
i = j
27+
break
28+
elif not code[j].isupper():
29+
return False
30+
elif code[i] == '<':
31+
for j in range(i + 1, i + 12):
32+
if j >= len(code) or j == i + 11 or (j == i + 1 and code[j] == '>'):
33+
return False
34+
elif code[j] == '>':
35+
tagstack.append(code[i + 1:j])
36+
i = j
37+
break
38+
elif not code[j].isupper():
39+
return False
40+
41+
i += 1
42+
43+
return tagstack == []

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@
412412
[587][587l] |[Erect the Fence][587] |![rs]
413413
[589][589l] |[N-ary Tree Preorder Traversal][589] |![py]
414414
[590][590l] |[N-ary Tree Postorder Traversal][590] |![py]
415+
[591][591l] |[Tag Validator][591] |![py]
415416
[592][592l] |[Fraction Addition and Subtraction][592] |![rs]
416417
[593][593l] |[Valid Square][593] |![rs]
417418
[594][594l] |[Longest Harmonious Subsequence][594] |![rs]
@@ -2099,6 +2100,7 @@
20992100
[587]:Problemset/0587-Erect%20the%20Fence/README.md#587-erect-the-fence
21002101
[589]:Problemset/0589-N-ary%20Tree%20Preorder%20Traversal/README.md#589-n-ary-tree-preorder-traversal
21012102
[590]:Problemset/0590-N-ary%20Tree%20Postorder%20Traversal/README.md#590-n-ary-tree-postorder-traversal
2103+
[591]:Problemset/0591-Tag%20Validator/README.md#591-tag-validator
21022104
[592]:Problemset/0592-Fraction%20Addition%20and%20Subtraction/README.md#592-fraction-addition-and-subtraction
21032105
[593]:Problemset/0593-Valid%20Square/README.md#593-valid-square
21042106
[594]:Problemset/0594-Longest%20Harmonious%20Subsequence/README.md#594-longest-harmonious-subsequence
@@ -3780,6 +3782,7 @@
37803782
[587l]:https://leetcode.com/problems/erect-the-fence/
37813783
[589l]:https://leetcode.com/problems/n-ary-tree-preorder-traversal/
37823784
[590l]:https://leetcode.com/problems/n-ary-tree-postorder-traversal/
3785+
[591l]:https://leetcode.com/problems/tag-validator/
37833786
[592l]:https://leetcode.com/problems/fraction-addition-and-subtraction/
37843787
[593l]:https://leetcode.com/problems/valid-square/
37853788
[594l]:https://leetcode.com/problems/longest-harmonious-subsequence/

README_CN.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@
412412
[587][587l] |[安装栅栏][587] |![rs]
413413
[589][589l] |[N叉树的前序遍历][589] |![py]
414414
[590][590l] |[N叉树的后序遍历][590] |![py]
415+
[591][591l] |[标签验证器][591] |![py]
415416
[592][592l] |[分数加减运算][592] |![rs]
416417
[593][593l] |[有效的正方形][593] |![rs]
417418
[594][594l] |[最长和谐子序列][594] |![rs]
@@ -2099,6 +2100,7 @@
20992100
[587]:Problemset/0587-Erect%20the%20Fence/README_CN.md#587-安装栅栏
21002101
[589]:Problemset/0589-N-ary%20Tree%20Preorder%20Traversal/README_CN.md#589-n叉树的前序遍历
21012102
[590]:Problemset/0590-N-ary%20Tree%20Postorder%20Traversal/README_CN.md#590-n叉树的后序遍历
2103+
[591]:Problemset/0591-Tag%20Validator/README_CN.md#591-标签验证器
21022104
[592]:Problemset/0592-Fraction%20Addition%20and%20Subtraction/README_CN.md#592-分数加减运算
21032105
[593]:Problemset/0593-Valid%20Square/README_CN.md#593-有效的正方形
21042106
[594]:Problemset/0594-Longest%20Harmonious%20Subsequence/README_CN.md#594-最长和谐子序列
@@ -3780,6 +3782,7 @@
37803782
[587l]:https://leetcode.cn/problems/erect-the-fence/
37813783
[589l]:https://leetcode.cn/problems/n-ary-tree-preorder-traversal/
37823784
[590l]:https://leetcode.cn/problems/n-ary-tree-postorder-traversal/
3785+
[591l]:https://leetcode.cn/problems/tag-validator/
37833786
[592l]:https://leetcode.cn/problems/fraction-addition-and-subtraction/
37843787
[593l]:https://leetcode.cn/problems/valid-square/
37853788
[594l]:https://leetcode.cn/problems/longest-harmonious-subsequence/

0 commit comments

Comments
 (0)