Skip to main content
Back to problems
#591
Hard Algorithms

Tag validator

String Stack
40.3% acceptance
Jan 13, 2026
181
654
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.

Solution

Rust
Time O(n)
Space O(n)
LeetCode
solution.rs
impl Solution {
  pub fn is_valid(code: String) -> bool {
    let n = code.len();
    let bytes = code.as_bytes();
    let mut stack: Vec<String> = Vec::new();
    let mut i = 0usize;
    while i < n {
      if i > 0 && stack.is_empty() { return false; }
      if bytes[i] == b'<' {
        if i + 9 <= n && &code[i..i+9] == "<![CDATA[" {
          if let Some(j) = code[i+9..].find("]]>") {
            i = i + 9 + j + 3;
          } else { return false; }
        } else if i + 1 < n && bytes[i+1] == b'/' {
          if let Some(j) = code[i+2..].find('>') {
            let tag = &code[i+2..i+2+j];
            if !Self::vtag(tag) { return false; }
            if stack.last().map(|s| s.as_str()) != Some(tag) { return false; }
            stack.pop();
            i = i + 2 + j + 1;
          } else { return false; }
        } else {
          if let Some(j) = code[i+1..].find('>') {
            let tag = &code[i+1..i+1+j];
            if !Self::vtag(tag) { return false; }
            stack.push(tag.to_string());
            i = i + 1 + j + 1;
          } else { return false; }
        }
      } else { i += 1; }
    }
    stack.is_empty()
  }
  fn vtag(t: &str) -> bool {
    let l = t.len(); l >= 1 && l <= 9 && t.bytes().all(|b| b.is_ascii_uppercase())
  }
}