★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公衆號:山青詠芝(shanqingyongzhi)
➤博客園地址:山青詠芝(https://www.cnblogs.com/strengthen/)
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址:http://www.javashuo.com/article/p-dpckabjr-me.html
➤若是連接不是山青詠芝的博客園地址,則多是爬取做者的文章。
➤原文已修改更新!強烈建議點擊原文地址閱讀!支持做者!支持原創!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★html
Given a string representing a code snippet, you need to implement a tag validator to parse the code and return whether it is valid. A code snippet is valid if all the following rules hold: git
<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.TAG_NAME
only contain upper-case letters, and has length in range [1,9]. Otherwise, the TAG_NAME
is invalid.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.<
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).<![CDATA[CDATA_CONTENT]]>
. The range of CDATA_CONTENT
is defined as the characters between <![CDATA[
and the first subsequent ]]>
.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.Valid Code Examples:github
Input: "<DIV>This is the first line <![CDATA[<div>]]></DIV>"
Output: True
Explanation:
The code is wrapped in a closed tag : <DIV> and </DIV>.
The TAG_NAME is valid, the TAG_CONTENT consists of some characters and cdata.
Although CDATA_CONTENT has unmatched start tag with invalid TAG_NAME, it should be considered as plain text, not parsed as tag.
So TAG_CONTENT is valid, and then the code is valid. Thus return true.
Input: "<DIV>>> ![cdata[]] <![CDATA[<div>]>]]>]]>>]</DIV>"
Output: True
Explanation:
We first separate the code into : start_tag|tag_content|end_tag.
start_tag -> "<DIV>"
end_tag -> "</DIV>"
tag_content could also be separated into : text1|cdata|text2.
text1 -> ">> ![cdata[]] "
cdata -> "<![CDATA[<div>]>]]>", where the CDATA_CONTENT is "<div>]>"
text2 -> "]]>>]"
The reason why start_tag is NOT "<DIV>>>" is because of the rule 6. The reason why cdata is NOT "<![CDATA[<div>]>]]>]]>" is because of the rule 7.
Invalid Code Examples:數組
Input: "<A> <B> </A> </B>" Output: False Explanation: Unbalanced. If "<A>" is closed, then "<B>" must be unmatched, and vice versa. Input: "<DIV> div tag is not closed <DIV>" Output: False Input: "<DIV> unmatched < </DIV>" Output: False Input: "<DIV> closed tags with invalid tag name <b>123</b> </DIV>" Output: False Input: "<DIV> unmatched tags with invalid tag name </1234567890> and <CDATA[[]]> </DIV>" Output: False Input: "<DIV> unmatched start tag <B> and unmatched end tag </C> </DIV>" Output: False
Note:微信
letters
, digits
, '<'
,'>'
,'/'
,'!'
,'['
,']'
and ' '
.給定一個表示代碼片斷的字符串,你須要實現一個驗證器來解析這段代碼,並返回它是否合法。合法的代碼片斷須要遵照如下的全部規則:app
<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(不必定合法)。<![CDATA[CDATA_CONTENT]]>
。CDATA_CONTENT
的範圍被定義成 <![CDATA[
和後續的第一個 ]]>
之間的字符。CDATA_CONTENT
能夠包含任意字符。cdata 的功能是阻止驗證器解析CDATA_CONTENT
,因此即便其中有一些字符能夠被解析爲標籤(不管合法仍是不合法),也應該將它們視爲常規字符。合法代碼的例子:ide
輸入: "<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
注意:spa
數字
, 字母, '<'
,'>'
,'/'
,'!'
,'['
,']'
和' '
。1 class Solution { 2 func isValid(_ code: String) -> Bool { 3 var st:[String] = [String]() 4 var i:Int = 0 5 while(i < code.count) 6 { 7 if i > 0 && st.isEmpty 8 { 9 return false 10 } 11 if code.subString(i, 9) == "<![CDATA[" 12 { 13 var j:Int = i + 9 14 i = code.find("]]>",j) 15 if i < 0 {return false} 16 i += 2 17 } 18 else if code.subString(i, 2) == "</" 19 { 20 var j:Int = i + 2 21 i = code.find(">",j) 22 if i < 0 {return false} 23 var tag:String = code.subString(j, i - j) 24 if st.isEmpty || st.last! != tag 25 { 26 return false 27 } 28 st.popLast() 29 } 30 else if code.subString(i, 1) == "<" 31 { 32 var j:Int = i + 1 33 i = code.find(">",j) 34 if i < 0 || i == j || i - j > 9 35 { 36 return false 37 } 38 for k in j..<i 39 { 40 if code[k] < "A" || code[k] > "Z" 41 { 42 return false 43 } 44 } 45 var tag:String = code.subString(j, i - j) 46 st.append(tag) 47 } 48 i += 1 49 } 50 return st.isEmpty 51 } 52 } 53 54 //String擴展 55 extension String { 56 //subscript函數能夠檢索數組中的值 57 //直接按照索引方式截取指定索引的字符 58 subscript (_ i: Int) -> Character { 59 //讀取字符 60 get {return self[index(startIndex, offsetBy: i)]} 61 } 62 63 // 截取字符串:指定索引和字符數 64 // - begin: 開始截取處索引 65 // - count: 截取的字符數量 66 func subString(_ begin:Int,_ count:Int) -> String { 67 let start = self.index(self.startIndex, offsetBy: max(0, begin)) 68 let end = self.index(self.startIndex, offsetBy: min(self.count, begin + count)) 69 return String(self[start..<end]) 70 } 71 72 // 截取字符串:從index到結束處 73 // - Parameter index: 開始索引 74 // - Returns: 子字符串 75 func subStringFrom(_ index: Int) -> String { 76 let theIndex = self.index(self.endIndex, offsetBy: index - self.count) 77 return String(self[theIndex..<endIndex]) 78 } 79 80 //從0索引處開始查找是否包含指定的字符串,返回Int類型的索引 81 //返回第一次出現的指定子字符串在此字符串中的索引 82 func find(_ sub:String)->Int { 83 var pos = -1 84 if let range = range(of:sub, options: .literal ) { 85 if !range.isEmpty { 86 pos = self.distance(from:startIndex, to:range.lowerBound) 87 } 88 } 89 return pos 90 } 91 92 //從指定索引處開始查找是否包含指定的字符串,返回Int類型的索引 93 //返回第一次出現的指定子字符串在此字符串中的索引 94 func find(_ sub:String,_ begin:Int)->Int { 95 var str:String = self.subStringFrom(begin) 96 var pos:Int = str.find(sub) 97 return pos == -1 ? -1 : (pos + begin) 98 } 99 }