Problem: 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:

1. The code must be wrapped in a valid closed tag. Otherwise, the code is invalid.
2. A closed tag (not necessarily valid) has exactly the following format : `<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.
3. A valid `TAG_NAME` only contain upper-case letters, and has length in range [1,9]. Otherwise, the `TAG_NAME` is invalid.
4. 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.
5. 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.
6. A `<` 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).
7. The cdata has the following format : `<![CDATA[CDATA_CONTENT]]>`. The range of `CDATA_CONTENT` is defined as the characters between `<![CDATA[` and the first subsequent `]]>`.
8. `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.


Python solution:
import math
import itertools
import bisect
import collections
import string
import heapq
import functools
import sortedcontainers
from typing import List, Dict, Tuple, Iterator

class Solution:
  def isValid(self, code: str) -> bool:
    if code[0] != '<' or code[-1] != '>':
      return False

    containsTag = False
    stack = []

    def isValidCdata(s: str) -> bool:
      return s.find('[CDATA[') == 0

    def isValidTagName(tagName: str, isEndTag: bool) -> bool:
      nonlocal containsTag
      if not tagName or len(tagName) > 9:
        return False
      if any(not c.isupper() for c in tagName):
        return False

      if isEndTag:
        return stack and stack.pop() == tagName

      containsTag = True
      stack.append(tagName)
      return True

    i = 0
    while i < len(code):
      if not stack and containsTag:
        return False
      if code[i] == '<':
        if stack and code[i + 1] == '!':
          closeIndex = code.find(']]>', i + 2)
          if closeIndex == -1 or not isValidCdata(code[i + 2:closeIndex]):
            return False
        elif code[i + 1] == '/':
          closeIndex = code.find('>', i + 2)
          if closeIndex == -1 or not isValidTagName(code[i + 2:closeIndex], True):
            return False
        else:
          closeIndex = code.find('>', i + 1)
          if closeIndex == -1 or not isValidTagName(code[i + 1:closeIndex], False):
            return False
        i = closeIndex
      i += 1

    return not stack and containsTag


Java solution:
class Solution {
  public boolean isValid(String code) {
    if (code.charAt(0) != '<' || code.charAt(code.length() - 1) != '>')
      return false;

    Deque<String> stack = new ArrayDeque<>();

    for (int i = 0; i < code.length(); ++i) {
      int closeIndex = 0;
      if (stack.isEmpty() && containsTag)
        return false;
      if (code.charAt(i) == '<') {
        // It's inside a tag, so check if it's a cdata.
        if (!stack.isEmpty() && code.charAt(i + 1) == '!') {
          closeIndex = code.indexOf("]]>", i + 2);
          if (closeIndex < 0 || !isValidCdata(code.substring(i + 2, closeIndex)))
            return false;
        } else if (code.charAt(i + 1) == '/') { // the end tag
          closeIndex = code.indexOf('>', i + 2);
          if (closeIndex < 0 || !isValidTagName(stack, code.substring(i + 2, closeIndex), true))
            return false;
        } else { // the start tag
          closeIndex = code.indexOf('>', i + 1);
          if (closeIndex < 0 || !isValidTagName(stack, code.substring(i + 1, closeIndex), false))
            return false;
        }
        i = closeIndex;
      }
    }

    return stack.isEmpty() && containsTag;
  }

  private boolean containsTag = false;

  private boolean isValidCdata(final String s) {
    return s.indexOf("[CDATA[") == 0;
  }

  private boolean isValidTagName(Deque<String> stack, String tagName, boolean isEndTag) {
    if (tagName.isEmpty() || tagName.length() > 9)
      return false;

    for (final char c : tagName.toCharArray())
      if (!Character.isUpperCase(c))
        return false;

    if (isEndTag)
      return !stack.isEmpty() && stack.pop().equals(tagName);

    containsTag = true;
    stack.push(tagName);
    return true;
  }
}


C++ solution:
class Solution {
 public:
  bool isValid(string code) {
    if (code[0] != '<' || code.back() != '>')
      return false;

    stack<string> stack;

    for (int i = 0; i < code.length(); ++i) {
      int closeIndex = 0;
      if (stack.empty() && containsTag)
        return false;
      if (code[i] == '<') {
        // It's inside a tag, so check if it's a cdata.
        if (!stack.empty() && code[i + 1] == '!') {
          closeIndex = code.find("]]>", i + 2);
          if (closeIndex == string::npos ||
              !isValidCdata(code.substr(i + 2, closeIndex - i - 2)))
            return false;
        } else if (code[i + 1] == '/') {  // the end tag
          closeIndex = code.find('>', i + 2);
          if (closeIndex == string::npos ||
              !isValidTagName(stack, code.substr(i + 2, closeIndex - i - 2),
                              true))
            return false;
        } else {  // the start tag
          closeIndex = code.find('>', i + 1);
          if (closeIndex == string::npos ||
              !isValidTagName(stack, code.substr(i + 1, closeIndex - i - 1),
                              false))
            return false;
        }
        i = closeIndex;
      }
    }

    return stack.empty() && containsTag;
  }

 private:
  bool containsTag = false;

  bool isValidCdata(const string& s) {
    return s.find("[CDATA[") == 0;
  }

  bool isValidTagName(stack<string>& stack, const string& tagName,
                      bool isEndTag) {
    if (tagName.empty() || tagName.length() > 9)
      return false;

    for (const char c : tagName)
      if (!isupper(c))
        return false;

    if (isEndTag) {
      if (stack.empty())
        return false;
      if (stack.top() != tagName)
        return false;
      stack.pop();
      return true;
    }

    containsTag = true;
    stack.push(tagName);
    return true;
  }
};