class Solution {
public:
bool isValid(string code) {
}
};
591. 标签验证器
给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:
<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
,所以即使其中有一些字符可以被解析为标签(无论合法还是不合法),也应该将它们视为常规字符。合法代码的例子:
输入: "<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
注意:
数字
, 字母, '<'
,'>'
,'/'
,'!'
,'['
,']'
和' '
。相似题目
原站题解
java 解法, 执行用时: 1 ms, 内存消耗: 39.6 MB, 提交时间: 2022-12-04 18:24:08
class Solution { public boolean isValid(String code) { int n = code.length(); Deque<String> tags = new ArrayDeque<String>(); int i = 0; while (i < n) { if (code.charAt(i) == '<') { if (i == n - 1) { return false; } if (code.charAt(i + 1) == '/') { int j = code.indexOf('>', i); if (j < 0) { return false; } String tagname = code.substring(i + 2, j); if (tags.isEmpty() || !tags.peek().equals(tagname)) { return false; } tags.pop(); i = j + 1; if (tags.isEmpty() && i != n) { return false; } } else if (code.charAt(i + 1) == '!') { if (tags.isEmpty()) { return false; } if (i + 9 > n) { return false; } String cdata = code.substring(i + 2, i + 9); if (!"[CDATA[".equals(cdata)) { return false; } int j = code.indexOf("]]>", i); if (j < 0) { return false; } i = j + 3; } else { int j = code.indexOf('>', i); if (j < 0) { return false; } String tagname = code.substring(i + 1, j); if (tagname.length() < 1 || tagname.length() > 9) { return false; } for (int k = 0; k < tagname.length(); ++k) { if (!Character.isUpperCase(tagname.charAt(k))) { return false; } } tags.push(tagname); i = j + 1; } } else { if (tags.isEmpty()) { return false; } ++i; } } return tags.isEmpty(); } }
golang 解法, 执行用时: 0 ms, 内存消耗: 1.9 MB, 提交时间: 2022-12-04 18:23:51
func isValid(code string) bool { tags := []string{} for code != "" { if code[0] != '<' { if len(tags) == 0 { return false } code = code[1:] continue } if len(code) == 1 { return false } if code[1] == '/' { j := strings.IndexByte(code, '>') if j == -1 { return false } if len(tags) == 0 || tags[len(tags)-1] != code[2:j] { return false } tags = tags[:len(tags)-1] code = code[j+1:] if len(tags) == 0 && code != "" { return false } } else if code[1] == '!' { if len(tags) == 0 || len(code) < 9 || code[2:9] != "[CDATA[" { return false } j := strings.Index(code, "]]>") if j == -1 { return false } code = code[j+3:] } else { j := strings.IndexByte(code, '>') if j == -1 { return false } tagName := code[1:j] if tagName == "" || len(tagName) > 9 { return false } for _, ch := range tagName { if !unicode.IsUpper(ch) { return false } } tags = append(tags, tagName) code = code[j+1:] } } return len(tags) == 0 }
javascript 解法, 执行用时: 76 ms, 内存消耗: 41 MB, 提交时间: 2022-12-04 18:23:37
/** * @param {string} code * @return {boolean} */ var isValid = function(code) { const n = code.length; const tags = []; let i = 0; while (i < n) { if (code[i] === '<') { if (i === n - 1) { return false; } if (code[i + 1] === '/') { const j = code.indexOf('>', i); if (j < 0) { return false; } const tagname = code.slice(i + 2, j); if (tags.length === 0 || tags[tags.length - 1] !== tagname) { return false; } tags.pop(); i = j + 1; if (tags.length === 0 && i !== n) { return false; } } else if (code[i + 1] === '!') { if (tags.length === 0) { return false; } if (i + 9 > n) { return false; } const cdata = code.slice(i + 2, i + 9); if ("[CDATA[" !== cdata) { return false; } const j = code.indexOf("]]>", i); if (j < 0) { return false; } i = j + 3; } else { const j = code.indexOf('>', i); if (j < 0) { return false; } const tagname = code.slice(i + 1, j); if (tagname.length < 1 || tagname.length > 9) { return false; } for (let k = 0; k < tagname.length; ++k) { if (!(tagname[k] >= 'A' && tagname[k] <= 'Z')) { return false; } } tags.push(tagname); i = j + 1; } } else { if (tags.length === 0) { return false; } ++i; } } return tags.length === 0; };
python3 解法, 执行用时: 40 ms, 内存消耗: 14.9 MB, 提交时间: 2022-12-04 18:23:17
class Solution: def isValid(self, code: str) -> bool: n = len(code) tags = list() i = 0 while i < n: if code[i] == "<": if i == n - 1: return False if code[i + 1] == "/": j = code.find(">", i) if j == -1: return False tagname = code[i+2:j] if not tags or tags[-1] != tagname: return False tags.pop() i = j + 1 if not tags and i != n: return False elif code[i + 1] == "!": if not tags: return False cdata = code[i+2:i+9] if cdata != "[CDATA[": return False j = code.find("]]>", i) if j == -1: return False i = j + 3 else: j = code.find(">", i) if j == -1: return False tagname = code[i+1:j] if not 1 <= len(tagname) <= 9 or not all(ch.isupper() for ch in tagname): return False tags.append(tagname) i = j + 1 else: if not tags: return False i += 1 return not tags