题目
标签验证器
思路
如果当前字符为 <,需要考虑下面四种情况:
- 如果下一个字符为 /,那么说明我们遇到了一个结束标签。我们需要定位下一个 > 的位置 j,此时
code[i+2..j−1]
就是该结束标签的名称。我们需要判断该名称与当前栈顶的名称是否匹配,如果匹配,说明名称的标签已经闭合,我们需要将当前栈顶的名称弹出。同时根据规则 1,我们需要保证整个 code 被闭合标签包围,因此如果栈中已经没有标签,但是 j 并不是 code 的末尾,那么说明后续还会有字符,它们不被闭合标签包围。
- 如果下一个字符为 !,那么说明我们遇到了一个 cdata,我们需要继续往后读 77 个字符,判断其是否为 \texttt{[CDATA[}[CDATA[。在这之后,我们定位下一个 ]]> 的位置 j,此时
code[i+9..j−1]
就是 cdata 中的内容,它不需要被解析,所以我们也不必进行任何验证。需要注意的是,根据规则 1,栈中需要存在至少一个开放的标签。
- 如果下一个字符为大写字母,那么说明我们遇到了一个开始标签。我们需要定位下一个 > 的位置 j,此时
code[i+2..j−1]
就是该开始标签的名称。我们需要判断该名称是否恰好由 1 至 9 个大写字母组成,如果是,说明该标签合法,我们需要将该名称放入栈顶。
- 除此之外,如果不存在下一个字符,或者下一个字符不属于上述三种情况,那么 code 是不合法的。
如果当前的字符为其它字符,那么根据规则 1,栈中需要存在至少一个开放的标签。
在遍历完成后,我们还需要保证此时栈中没有任何(还没有结束的)标签。
题解
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
| class Solution { public: bool isValid(string code) { stack<string> stk; int i = 0, n = code.size(); while(i < n){ if(code[i] == '<'){ if(i == n - 1) return false; else if(code[i + 1] >= 'A' && code[i + 1] <= 'Z'){ int j = code.find('>', i); if(j == string::npos) return false; string str = code.substr(i + 1, j - i - 1); if(str.length() < 1 || str.length() > 9) return false; for(int k = 0; k < str.size(); k ++){ if(str[k] < 'A' || str[k] > 'Z') return false; } stk.push(str); i = j + 1; } else if(code[i + 1] == '/'){ int j = code.find('>', i); if(j == string::npos) return false; string str = code.substr(i + 2, j - i - 2); if(stk.empty() || stk.top() != str) return false; stk.pop(); i = j + 1; if(stk.empty() && i != n) return false; } else if(code[i + 1] == '!'){ if(stk.empty()) return false; string str = code.substr(i + 2, 7); if(str != "[CDATA[") return false; int j = code.find("]]>", i); if(j == string::npos) return false; i = j + 1; } else return false; } else{ if(stk.empty()) return false; i++; } } return stk.empty(); } };
|
- 时间复杂度:O(n),其中 n 为 code 的长度,我们只需要对 code 进行一次遍历。
- 空间复杂度:O(n),即为栈存储标签名称需要使用的空间。
执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:6.3 MB, 在所有 C++ 提交中击败了96.00%的用户
通过测试用例:260 / 260