本文涉及知识点
栈
LeetCode591. 标签验证器
给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:
代码必须被合法的闭合标签包围。否则,代码是无效的。
闭合标签(不一定合法)要严格符合格式:<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,所以即使其中有一些字符可以被解析为标签(无论合法还是不合法),也应该将它们视为常规字符。
合法代码的例子:
输入: “
输出: True
解释:
代码被包含在了闭合的标签内:
TAG_NAME 是合法的,TAG_CONTENT 包含了一些字符和 cdata 。
即使 CDATA_CONTENT 含有不匹配的起始标签和不合法的 TAG_NAME,它应该被视为普通的文本,而不是标签。
所以 TAG_CONTENT 是合法的,因此代码是合法的。最终返回True。
输入: “
输出: True
解释:
我们首先将代码分割为: start_tag|tag_content|end_tag 。
start_tag -> “
end_tag -> “”
tag_content 也可被分割为: text1|cdata|text2 。
text1 -> ">> ![cdata[]] "
cdata -> “<![CDATA[
text2 -> “]]>>]”
start_tag 不是 “
cdata 不是 “<![CDATA[
不合法代码的例子:
输入: “ ”
输出: False
解释: 不合法。如果 “” 是闭合的,那么 “” 一定是不匹配的,反之亦然。
输入: “
输出: False
输入: “
输出: False
输入: “
输出: False
输入: “
输出: False
输入: “
输出: False
注意:
为简明起见,你可以假设输入的代码(包括提到的任意字符)只包含数字, 字母, ‘<’,‘>’,‘/’,‘!’,‘[’,']‘和’ '。
栈
先处理CDATA,把[CDATA[ 和后续的第一个 ]]> 及之间内容 以外的内容全 复制到字符串str。此想法是错误的:如:“<![CDATA[wahaha]]]><![CDATA[]> wahaha]]>”
解析到<TAG_NAME> 时,将TAG_NAMe入栈。</TAG_NAME> 如果栈顶元素不是TAG_NAME,则解析失败。出栈。 其它字符,只要栈不为空,就可以了。除非是第0个字符,否则栈不能为空。
代码
核心代码
class Solution {
public:
bool isValid(string code) {
stack<string> sta;
for (int i = 0; i < code.length(); i++) {
if ((0!=i)&&sta.empty()) { return false; }
if ('<' != code[i]) {
continue;
}
int pos = code.find('>',i);
if (-1 == pos) {
return false; }
if (i + 1 == pos) {
return false; }
int iType = 0 ;
if (code[i + 1] == '/') {
iType = 1;
i++;
}
else if (code[i + 1] == '!')
{
int pos2 = code.find("]]>", i);
if (-1 == pos2) { return false; }
const int len = pos2 - i;
const string strLeft = "<![CDATA[";
if (len < strLeft.length()) { return false; }
if (code.substr(i, strLeft.length()) != strLeft) {
return false;
}
i = pos2 + strlen("]]>") - 1;
continue;
}
string strName;
for (i++; i < pos; i++) {
if ((code[i] < 'A') || (code[i] > 'Z')) {
return false; }
strName += code[i];
}
if ((strName.length() < 1) || (strName.length() > 9)) {
return false; }
if (1 == iType) {
if (sta.empty() || (strName != sta.top())) {
return false; }
sta.pop();
}
else {
sta.emplace(strName);
}
}
return sta.empty();
}
};
单元测试
template<class T1,class T2>
void AssertEx(const T1& t1, const T2& t2)
{
Assert::AreEqual(t1 , t2);
}
template<class T>
void AssertEx(const vector<T>& v1, const vector<T>& v2)
{
Assert::AreEqual(v1.size(), v2.size());
for (int i = 0; i < v1.size(); i++)
{
Assert::AreEqual(v1[i], v2[i]);
}
}
template<class T>
void AssertV2(vector<vector<T>> vv1, vector<vector<T>> vv2)
{
sort(vv1.begin(), vv1.end());
sort(vv2.begin(), vv2.end());
Assert::AreEqual(vv1.size(), vv2.size());
for (int i = 0; i < vv1.size(); i++)
{
AssertEx(vv1[i], vv2[i]);
}
}
namespace UnitTest
{
string code;
TEST_CLASS(UnitTest)
{
public:
TEST_METHOD(TestMethod0)
{
code = "<DIV>This is the first line <![CDATA[<div>]]></DIV>";
auto res = Solution().isValid(code);
AssertEx(true,res);
}
TEST_METHOD(TestMethod1)
{
code = "<DIV>>> ![cdata[]] <![CDATA[<div>]>]]>]]>>]</DIV>";
auto res = Solution().isValid(code);
AssertEx(true, res);
}
TEST_METHOD(TestMethod2)
{
code = "<A> <B> </A> </B>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
TEST_METHOD(TestMethod3)
{
code = "<DIV> div tag is not closed <DIV>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
TEST_METHOD(TestMethod4)
{
code = "<DIV> unmatched < </DIV>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
TEST_METHOD(TestMethod5)
{
code = "<DIV> closed tags with invalid tag name <b>123</b> </DIV>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
TEST_METHOD(TestMethod6)
{
code = "<DIV> unmatched tags with invalid tag name </1234567890> and <CDATA[[]]> </DIV>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
TEST_METHOD(TestMethod7)
{
code = "<DIV> unmatched start tag <B> and unmatched end tag </C> </DIV>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
TEST_METHOD(TestMethod8)
{
code = "<TRUE><![CDATA[wahaha]]]><![CDATA[]> wahaha]]></TRUE>";
auto res = Solution().isValid(code);
AssertEx(true, res);
}
TEST_METHOD(TestMethod9)
{
code = "<![CDATA[wahaha]]]><![CDATA[]> wahaha]]>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
TEST_METHOD(TestMethod10)
{
code = "<A></A><B></B>";
auto res = Solution().isValid(code);
AssertEx(false, res);
}
};
}