#include #include #include #include #include #include /** * 591. Tag Validator * Given a string representing a code snippet, 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: * * The code must be wrapped in a valid closed tag. Otherwise, the code is invalid. * A closed tag (not necessarily valid) has exactly the following format : TAG_CONTENT. Among them, is the start tag, and 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. * A valid TAG_NAME only contain upper-case letters, and has length in range [1,9]. Otherwise, the TAG_NAME is invalid. * A valid 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. * A start tag is unmatched if no end tag exists with the same TAG_NAME, and vice versa. However, you also need to consider the issue of unbalanced when tags are nested. * A < is unmatched if you cannot find a subsequent >. And when you find a < or should be parsed as TAG_NAME (not necessarily valid). * The cdata has the following format : . The range of CDATA_CONTENT is defined as the characters between . * 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. */ class Solution { public: static bool isValid(const std::string& code) { std::stack t; const int n = code.length(); for (int i = 0; i < n; ++i) { if (i && t.empty()) return false; if (code[i] != '<') { continue; } else if (code.substr(i + 1, 8) == "![CDATA[") { auto next = code.find("]]>", i + 9); if (next == std::string::npos) return false; i = next + 2; continue; // Then +1, next i at char after > } auto next = code.find('>', i + 1); if (code[i + 1] == '/') { // closing tag auto ct = code.substr(i + 2, next - (i + 2)); if (t.empty() || t.top() != ct) return false; t.pop(); } else { // new tag if (next - (i + 1) > 9 || next <= i + 1) return false; auto ct = code.substr(i + 1, next - (i + 1)); if (!std::all_of(ct.begin(), ct.end(), [](char ch){ return std::isupper(ch); })) return false; t.push(ct); } i = next; } return t.empty(); } }; int main() { const std::vector ss { "
This is the first line ]]>
", "
>> ![cdata[]] ]>]]>]]>>]
", " ", "
div tag is not closed
", "
unmatched <
", "
closed tags with invalid tag name 123
", "
unmatched tags with invalid tag name and
", "
unmatched start tag and unmatched end tag
" }; for (const auto& str : ss) std::cout << (Solution::isValid(str) ? "true" : "false") << "\n"; }