|
| 1 | +/** |
| 2 | + * 591. Tag Validator |
| 3 | + * https://leetcode.com/problems/tag-validator/ |
| 4 | + * Difficulty: Hard |
| 5 | + * |
| 6 | + * Given a string representing a code snippet, implement a tag validator to parse the code and |
| 7 | + * return whether it is valid. |
| 8 | + * |
| 9 | + * A code snippet is valid if all the following rules hold: |
| 10 | + * 1. The code must be wrapped in a valid closed tag. Otherwise, the code is invalid. |
| 11 | + * 2. A closed tag (not necessarily valid) has exactly the following format: |
| 12 | + * <TAG_NAME>TAG_CONTENT</TAG_NAME>. Among them, <TAG_NAME> is the start tag, and </TAG_NAME> |
| 13 | + * is the end tag. The TAG_NAME in start and end tags should be the same. A closed tag is valid |
| 14 | + * if and only if the TAG_NAME and TAG_CONTENT are valid. |
| 15 | + * 3. A valid TAG_NAME only contain upper-case letters, and has length in range [1,9]. Otherwise, |
| 16 | + * the TAG_NAME is invalid. |
| 17 | + * 4. A valid TAG_CONTENT may contain other valid closed tags, cdata and any characters (see |
| 18 | + * note1) EXCEPT unmatched <, unmatched start and end tag, and unmatched or closed tags with |
| 19 | + * invalid TAG_NAME. Otherwise, the TAG_CONTENT is invalid. |
| 20 | + * 5. A start tag is unmatched if no end tag exists with the same TAG_NAME, and vice versa. |
| 21 | + * However, you also need to consider the issue of unbalanced when tags are nested. |
| 22 | + * 6. A < is unmatched if you cannot find a subsequent >. And when you find a < or </, all |
| 23 | + * the subsequent characters until the next > should be parsed as TAG_NAME (not necessarily |
| 24 | + * valid). |
| 25 | + * 7. The cdata has the following format : <![CDATA[CDATA_CONTENT]]>. The range of CDATA_CONTENT |
| 26 | + * is defined as the characters between <![CDATA[ and the first subsequent ]]>. |
| 27 | + * 8. CDATA_CONTENT may contain any characters. The function of cdata is to forbid the validator |
| 28 | + * to parse CDATA_CONTENT, so even it has some characters that can be parsed as tag (no matter |
| 29 | + * valid or invalid), you should treat it as regular characters. |
| 30 | + */ |
| 31 | + |
| 32 | +/** |
| 33 | + * @param {string} code |
| 34 | + * @return {boolean} |
| 35 | + */ |
| 36 | +var isValid = function(code) { |
| 37 | + const stack = []; |
| 38 | + |
| 39 | + for (let i = 0; i < code.length;) { |
| 40 | + if (i > 0 && !stack.length) { |
| 41 | + return false; |
| 42 | + } |
| 43 | + if (code.startsWith('<![CDATA[', i)) { |
| 44 | + const j = i + 9; |
| 45 | + i = code.indexOf(']]>', j); |
| 46 | + if (i === -1) { |
| 47 | + return false; |
| 48 | + } |
| 49 | + i += 3; |
| 50 | + } else if (code.startsWith('</', i)) { |
| 51 | + const j = i + 2; |
| 52 | + i = code.indexOf('>', j); |
| 53 | + if (i === -1) { |
| 54 | + return false; |
| 55 | + } |
| 56 | + const tag = code.slice(j, i); |
| 57 | + if (!stack.length || stack.pop() !== tag || !/^[A-Z]{1,9}$/.test(tag)) { |
| 58 | + return false; |
| 59 | + } |
| 60 | + i++; |
| 61 | + } else if (code[i] === '<') { |
| 62 | + const j = i + 1; |
| 63 | + i = code.indexOf('>', j); |
| 64 | + if (i === -1) { |
| 65 | + return false; |
| 66 | + } |
| 67 | + const tag = code.slice(j, i); |
| 68 | + if (!/^[A-Z]{1,9}$/.test(tag)) { |
| 69 | + return false; |
| 70 | + } |
| 71 | + stack.push(tag); |
| 72 | + i++; |
| 73 | + } else { |
| 74 | + i++; |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + return !stack.length; |
| 79 | +}; |
0 commit comments