LeetCode Solutions
591. Tag Validator
Time: $O(n)$ Space: $O(n)$
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] == '<') {
// Inside a tag, so we can 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] == '/') { // End tag
closeIndex = code.find('>', i + 2);
if (closeIndex == string::npos ||
!isValidTagName(stack, code.substr(i + 2, closeIndex - i - 2),
true))
return false;
} else { // 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;
}
};
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) == '<') {
// Inside a tag, so we can 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) == '/') { // End tag
closeIndex = code.indexOf('>', i + 2);
if (closeIndex < 0 || !isValidTagName(stack, code.substring(i + 2, closeIndex), true))
return false;
} else { // 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;
}
}
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] == '<':
# Inside a tag, so we can check if it's a cdata
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] == '/': # End tag
closeIndex = code.find('>', i + 2)
if closeIndex == -1 or not isValidTagName(code[i + 2:closeIndex], True):
return False
else: # Start tag
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