栈
字符串
题目描述
给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:
代码必须被合法的闭合标签 包围。否则,代码是无效的。
闭合标签 (不一定合法)要严格符合格式:<TAG_NAME>TAG_CONTENT</TAG_NAME>
。其中,<TAG_NAME>
是起始标签,</TAG_NAME>
是结束标签。起始和结束标签中的 TAG_NAME 应当相同。当且仅当 TAG_NAME 和 TAG_CONTENT 都是合法的,闭合标签才是合法的 。
合法的 TAG_NAME
仅含有大写字母 ,长度在范围 [1,9] 之间。否则,该 TAG_NAME
是不合法的 。
合法的 TAG_CONTENT
可以包含其他合法的闭合标签 ,cdata (请参考规则7)和任意字符(注意参考规则1)除了 不匹配的<
、不匹配的起始和结束标签、不匹配的或带有不合法 TAG_NAME 的闭合标签。否则,TAG_CONTENT
是不合法的 。
一个起始标签,如果没有具有相同 TAG_NAME 的结束标签与之匹配,是不合法的。反之亦然。不过,你也需要考虑标签嵌套的问题。
一个<
,如果你找不到一个后续的>
与之匹配,是不合法的。并且当你找到一个<
或</
时,所有直到下一个>
的前的字符,都应当被解析为 TAG_NAME(不一定合法)。
cdata 有如下格式:<![CDATA[CDATA_CONTENT]]>
。CDATA_CONTENT
的范围被定义成 <![CDATA[
和后续的第一个 ]]>
之间的字符。
CDATA_CONTENT
可以包含任意字符 。cdata 的功能是阻止验证器解析CDATA_CONTENT
,所以即使其中有一些字符可以被解析为标签(无论合法还是不合法),也应该将它们视为常规字符 。
合法代码的例子:
输入: "<DIV>This is the first line <![CDATA[<div>]]></DIV>"
输出: True
解释:
代码被包含在了闭合的标签内: <DIV> 和 </DIV> 。
TAG_NAME 是合法的,TAG_CONTENT 包含了一些字符和 cdata 。
即使 CDATA_CONTENT 含有不匹配的起始标签和不合法的 TAG_NAME,它应该被视为普通的文本,而不是标签。
所以 TAG_CONTENT 是合法的,因此代码是合法的。最终返回True。
输入: "<DIV>>> ![cdata[]] <![CDATA[<div>]>]]>]]>>]</DIV>"
输出: True
解释:
我们首先将代码分割为: start_tag|tag_content|end_tag 。
start_tag -> "<DIV>"
end_tag -> "</DIV>"
tag_content 也可被分割为: text1|cdata|text2 。
text1 -> ">> ![cdata[]] "
cdata -> "<![CDATA[<div>]>]]>" ,其中 CDATA_CONTENT 为 "<div>]>"
text2 -> "]]>>]"
start_tag 不 是 "<DIV>>>" 的原因参照规则 6 。
cdata 不 是 "<![CDATA[<div>]>]]>]]>" 的原因参照规则 7 。
不合法代码的例子:
输入: "<A> <B> </A> </B>"
输出: False
解释: 不合法。如果 "<A>" 是闭合的,那么 "<B>" 一定是不匹配的,反之亦然。
输入: "<DIV> div tag is not closed <DIV>"
输出: False
输入: "<DIV> unmatched < </DIV>"
输出: False
输入: "<DIV> closed tags with invalid tag name <b>123</b> </DIV>"
输出: False
输入: "<DIV> unmatched tags with invalid tag name </1234567890> and <CDATA[[]]> </DIV>"
输出: False
输入: "<DIV> unmatched start tag <B> and unmatched end tag </C> </DIV>"
输出: False
注意:
为简明起见,你可以假设输入的代码(包括提到的任意字符 )只包含数字
, 字母 , '<'
,'>'
,'/'
,'!'
,'['
,']'
和' '
。
解法
方法一:栈模拟
Python3 Java C++ Go Rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 class Solution :
def isValid ( self , code : str ) -> bool :
def check ( tag ):
return 1 <= len ( tag ) <= 9 and all ( c . isupper () for c in tag )
stk = []
i , n = 0 , len ( code )
while i < n :
if i and not stk :
return False
if code [ i : i + 9 ] == '<![CDATA[' :
i = code . find ( ']]>' , i + 9 )
if i < 0 :
return False
i += 2
elif code [ i : i + 2 ] == '</' :
j = i + 2
i = code . find ( '>' , j )
if i < 0 :
return False
t = code [ j : i ]
if not check ( t ) or not stk or stk . pop () != t :
return False
elif code [ i ] == '<' :
j = i + 1
i = code . find ( '>' , j )
if i < 0 :
return False
t = code [ j : i ]
if not check ( t ):
return False
stk . append ( t )
i += 1
return not stk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 class Solution {
public boolean isValid ( String code ) {
Deque < String > stk = new ArrayDeque <> ();
for ( int i = 0 ; i < code . length (); ++ i ) {
if ( i > 0 && stk . isEmpty ()) {
return false ;
}
if ( code . startsWith ( "<![CDATA[" , i )) {
i = code . indexOf ( "]]>" , i + 9 );
if ( i < 0 ) {
return false ;
}
i += 2 ;
} else if ( code . startsWith ( "</" , i )) {
int j = i + 2 ;
i = code . indexOf ( ">" , j );
if ( i < 0 ) {
return false ;
}
String t = code . substring ( j , i );
if ( ! check ( t ) || stk . isEmpty () || ! stk . pop (). equals ( t )) {
return false ;
}
} else if ( code . startsWith ( "<" , i )) {
int j = i + 1 ;
i = code . indexOf ( ">" , j );
if ( i < 0 ) {
return false ;
}
String t = code . substring ( j , i );
if ( ! check ( t )) {
return false ;
}
stk . push ( t );
}
}
return stk . isEmpty ();
}
private boolean check ( String tag ) {
int n = tag . length ();
if ( n < 1 || n > 9 ) {
return false ;
}
for ( char c : tag . toCharArray ()) {
if ( ! Character . isUpperCase ( c )) {
return false ;
}
}
return true ;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 class Solution {
public :
bool isValid ( string code ) {
stack < string > stk ;
for ( int i = 0 ; i < code . size (); ++ i ) {
if ( i && stk . empty ()) return false ;
if ( code . substr ( i , 9 ) == "<![CDATA[" ) {
i = code . find ( "]]>" , i + 9 );
if ( i < 0 ) return false ;
i += 2 ;
} else if ( code . substr ( i , 2 ) == "</" ) {
int j = i + 2 ;
i = code . find ( '>' , j );
if ( i < 0 ) return false ;
string t = code . substr ( j , i - j );
if ( ! check ( t ) || stk . empty () || stk . top () != t ) return false ;
stk . pop ();
} else if ( code . substr ( i , 1 ) == "<" ) {
int j = i + 1 ;
i = code . find ( '>' , j );
if ( i < 0 ) return false ;
string t = code . substr ( j , i - j );
if ( ! check ( t )) return false ;
stk . push ( t );
}
}
return stk . empty ();
}
bool check ( string tag ) {
int n = tag . size ();
if ( n < 1 || n > 9 ) return false ;
for ( char & c : tag )
if ( ! isupper ( c ))
return false ;
return true ;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 func isValid ( code string ) bool {
var stk [] string
for i := 0 ; i < len ( code ); i ++ {
if i > 0 && len ( stk ) == 0 {
return false
}
if strings . HasPrefix ( code [ i :], "<![CDATA[" ) {
n := strings . Index ( code [ i + 9 :], "]]>" )
if n == - 1 {
return false
}
i += n + 11
} else if strings . HasPrefix ( code [ i :], "</" ) {
if len ( stk ) == 0 {
return false
}
j := i + 2
n := strings . IndexByte ( code [ j :], '>' )
if n == - 1 {
return false
}
t := code [ j : j + n ]
last := stk [ len ( stk ) - 1 ]
stk = stk [: len ( stk ) - 1 ]
if ! check ( t ) || last != t {
return false
}
i += n + 2
} else if strings . HasPrefix ( code [ i :], "<" ) {
j := i + 1
n := strings . IndexByte ( code [ j :], '>' )
if n == - 1 {
return false
}
t := code [ j : j + n ]
if ! check ( t ) {
return false
}
stk = append ( stk , t )
i += n + 1
}
}
return len ( stk ) == 0
}
func check ( tag string ) bool {
n := len ( tag )
if n < 1 || n > 9 {
return false
}
for _ , c := range tag {
if c < 'A' || c > 'Z' {
return false
}
}
return true
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 impl Solution {
pub fn is_valid ( code : String ) -> bool {
fn check ( tag : & str ) -> bool {
let n = tag . len ();
n >= 1 && n <= 9 && tag . as_bytes (). iter (). all ( | b | b . is_ascii_uppercase ())
}
let mut stk = Vec :: new ();
let mut i = 0 ;
while i < code . len () {
if i > 0 && stk . is_empty () {
return false ;
}
if code [ i .. ]. starts_with ( "<![CDATA[" ) {
match code [ i + 9 .. ]. find ( "]]>" ) {
Some ( n ) => {
i += n + 11 ;
}
None => {
return false ;
}
};
} else if code [ i .. ]. starts_with ( "</" ) {
let j = i + 2 ;
match code [ j .. ]. find ( '>' ) {
Some ( n ) => {
let t = & code [ j .. j + n ];
if ! check ( t ) || stk . is_empty () || stk . pop (). unwrap () != t {
return false ;
}
i += n + 2 ;
}
None => {
return false ;
}
};
} else if code [ i .. ]. starts_with ( "<" ) {
let j = i + 1 ;
match code [ j .. ]. find ( '>' ) {
Some ( n ) => {
let t = & code [ j .. j + n ];
if ! check ( t ) {
return false ;
}
stk . push ( t );
}
None => {
return false ;
}
};
}
i += 1 ;
}
stk . is_empty ()
}
}
GitHub