字符串 
      
    
      
      
      
        栈 
      
    
   
  
    
      
       
     
  
  
    
      
    
    
      
       
     
  
题目描述 
给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:
    代码必须被合法的闭合标签 包围。否则,代码是无效的。 
    闭合标签 (不一定合法)要严格符合格式:<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