欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > LeetCode题练习与总结:标签验证器--591

LeetCode题练习与总结:标签验证器--591

2025/5/5 13:19:40 来源:https://blog.csdn.net/weixin_62860386/article/details/145384341  浏览:    关键词:LeetCode题练习与总结:标签验证器--591

一、题目描述

给定一个表示代码片段的字符串,你需要实现一个验证器来解析这段代码,并返回它是否合法。合法的代码片段需要遵守以下的所有规则:

  1. 代码必须被合法的闭合标签包围。否则,代码是无效的。
  2. 闭合标签(不一定合法)要严格符合格式:<TAG_NAME>TAG_CONTENT</TAG_NAME>。其中,<TAG_NAME>是起始标签,</TAG_NAME>是结束标签。起始和结束标签中的 TAG_NAME 应当相同。当且仅当 TAG_NAME 和 TAG_CONTENT 都是合法的,闭合标签才是合法的
  3. 合法的 TAG_NAME 仅含有大写字母,长度在范围 [1,9] 之间。否则,该 TAG_NAME 是不合法的
  4. 合法的 TAG_CONTENT 可以包含其他合法的闭合标签cdata (请参考规则7)和任意字符(注意参考规则1)除了不匹配的<、不匹配的起始和结束标签、不匹配的或带有不合法 TAG_NAME 的闭合标签。否则,TAG_CONTENT 是不合法的
  5. 一个起始标签,如果没有具有相同 TAG_NAME 的结束标签与之匹配,是不合法的。反之亦然。不过,你也需要考虑标签嵌套的问题。
  6. 一个<,如果你找不到一个后续的>与之匹配,是不合法的。并且当你找到一个<</时,所有直到下一个>的前的字符,都应当被解析为 TAG_NAME(不一定合法)。
  7. cdata 有如下格式:<![CDATA[CDATA_CONTENT]]>CDATA_CONTENT 的范围被定义成 <![CDATA[ 和后续的第一个 ]]>之间的字符。
  8. CDATA_CONTENT 可以包含任意字符。cdata 的功能是阻止验证器解析CDATA_CONTENT,所以即使其中有一些字符可以被解析为标签(无论合法还是不合法),也应该将它们视为常规字符

合法代码的例子:

输入: "<DIV>This is the first line <![CDATA[<div>]]></DIV>"输出: True解释: 代码被包含在了闭合的标签内: <DIV> 和 </DIV> 。TAG_NAME 是合法的,TAG_CONTENT 包含了一些字符和 cdata 。 即使 CDATA_CONTENT 含有不匹配的起始标签和不合法的 TAG_NAME,它应该被视为普通的文本,而不是标签。所以 TAG_CONTENT 是合法的,因此代码是合法的。最终返回True。输入: "<DIV>>>  ![cdata[]] <![CDATA[<div>]>]]>]]>>]</DIV>"输出: True解释:我们首先将代码分割为: start_tag|tag_content|end_tag 。start_tag -> "<DIV>"end_tag -> "</DIV>"tag_content 也可被分割为: text1|cdata|text2 。text1 -> ">>  ![cdata[]] "cdata -> "<![CDATA[<div>]>]]>" ,其中 CDATA_CONTENT 为 "<div>]>"text2 -> "]]>>]"start_tag "<DIV>>>" 的原因参照规则 6 。
cdata "<![CDATA[<div>]>]]>]]>" 的原因参照规则 7 。

不合法代码的例子:

输入: "<A>  <B> </A>   </B>"
输出: False
解释: 不合法。如果 "<A>" 是闭合的,那么 "<B>" 一定是不匹配的,反之亦然。输入: "<DIV>  div tag is not closed  <DIV>"
输出: False输入: "<DIV>  unmatched <  </DIV>"
输出: False输入: "<DIV> closed tags with invalid tag name  <b>123</b> </DIV>"
输出: False输入: "<DIV> unmatched tags with invalid tag name  </1234567890> and <CDATA[[]]>  </DIV>"
输出: False输入: "<DIV>  unmatched start tag <B>  and unmatched end tag </C>  </DIV>"
输出: False

注意:

  1. 为简明起见,你可以假设输入的代码(包括提到的任意字符)只包含数字字母'<','>','/','!','[',']'' '

二、解题思路

  • 首先,我们需要定义一个函数来检查一个字符串是否是合法的标签名。这个函数应该检查字符串是否只包含大写字母,并且长度在1到9之间。

  • 我们需要一个辅助函数来解析字符串并找到下一个标签的开始位置。这个函数应该返回标签的类型(起始标签、结束标签或CDATA),标签名,以及下一个标签的开始位置。

  • 使用一个栈来管理标签。当我们遇到一个起始标签时,将其推入栈中;当我们遇到一个结束标签时,检查栈顶元素是否与结束标签匹配,如果匹配则弹出栈顶元素。

  • 解析字符串时,我们需要处理以下几种情况:

    • 如果遇到<,我们需要判断是起始标签、结束标签还是CDATA。
    • 如果是起始标签或结束标签,我们需要检查标签名是否合法,并相应地处理栈。
    • 如果是CDATA,我们需要找到CDATA的结束位置,并跳过其中的内容。
    • 如果遇到其他字符,我们需要确保它们不是不匹配的<
  • 在解析结束后,如果栈为空,则代码是合法的;否则,它是非法的。

三、具体代码

import java.util.*;public class Solution {public boolean isValid(String code) {Stack<String> stack = new Stack<>();int i = 0;while (i < code.length()) {if (code.charAt(i) == '<') {if (i == code.length() - 1) return false; // '<' is the last characterif (code.charAt(i + 1) == '/') { // End tagint j = code.indexOf('>', i);if (j == -1) return false; // No matching '>'String tagName = code.substring(i + 2, j);if (!isValidTagName(tagName) || stack.isEmpty() || !stack.pop().equals(tagName)) {return false;}i = j + 1;} else if (code.charAt(i + 1) == '!') { // CDATAif (!stack.isEmpty() && code.startsWith("<![CDATA[", i)) {int j = code.indexOf("]]>", i);if (j == -1) return false; // No matching ']]>'i = j + 3;} else {return false;}} else { // Start tagint j = code.indexOf('>', i);if (j == -1) return false; // No matching '>'String tagName = code.substring(i + 1, j);if (!isValidTagName(tagName)) return false;stack.push(tagName);i = j + 1;}} else {i++;}}return stack.isEmpty();}private boolean isValidTagName(String tagName) {return tagName.matches("[A-Z]{1,9}");}
}

这段代码定义了一个Solution类,其中包含isValid方法用于检查代码是否合法,以及一个辅助方法isValidTagName用于检查标签名是否合法。代码逻辑按照上述解题思路实现。

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 我们定义code.length()n,这是输入字符串的长度。

  • 我们有一个主循环,它遍历字符串code的每个字符。这个循环最多执行n次。

  • 在每次循环中,我们可能会执行以下操作:

    • 检查是否是起始标签、结束标签或CDATA,这涉及到字符串比较,其时间复杂度为O(1)。
    • 查找下一个>字符的位置,使用indexOf方法,其时间复杂度为O(n)。
    • 检查标签名是否合法,使用正则表达式匹配,其时间复杂度为O(1),因为标签名长度限制在1到9之间。
    • 向栈中推入或弹出元素,其时间复杂度为O(1)。
  • 由于indexOf方法在每次遇到<字符时都会被调用,并且在最坏情况下,它可能需要遍历整个字符串,因此,整个算法的时间复杂度主要取决于indexOf方法的调用次数和每次调用的成本。

  • 在最坏情况下,indexOf方法可能在每次循环迭代中被调用一次,总共有n次迭代,因此时间复杂度为O(n^2)。

2. 空间复杂度
  • 我们使用了一个栈stack来存储标签名。

  • 在最坏的情况下,如果所有标签都是嵌套的,那么栈的大小可能达到n/2(假设每个标签都是成对的,并且每个标签长度为2,这是最紧凑的情况)。

  • 因此,空间复杂度为O(n),其中n是输入字符串的长度。

五、总结知识点

  • Java 类的定义

    • public class Solution 定义了一个公共类 Solution
  • Java 方法的定义

    • public boolean isValid(String code) 定义了一个公共方法 isValid,它接受一个字符串参数并返回一个布尔值。
  • Java 栈的使用

    • Stack<String> stack = new Stack<>(); 创建了一个字符串类型的栈,用于管理标签。
  • 字符串操作

    • 使用 charAt(int index) 方法来获取字符串中特定位置的字符。
    • 使用 indexOf(char ch) 方法来查找字符在字符串中的位置。
    • 使用 substring(int beginIndex, int endIndex) 方法来获取字符串的子串。
  • 循环和条件语句

    • 使用 while 循环来遍历字符串。
    • 使用 if-else 语句来处理不同类型的标签(起始标签、结束标签、CDATA)。
  • 正则表达式

    • 使用 matches(String regex) 方法来检查字符串是否符合给定的正则表达式模式。
  • 异常处理

    • 检查字符串是否以特定的子串开始,例如 startsWith(String prefix)
    • 检查字符串是否在特定位置结束,例如检查是否没有匹配的结束标签 >
  • 栈操作

    • 使用 push(E element) 方法将元素推入栈。
    • 使用 pop() 方法从栈中移除并返回栈顶元素。
    • 使用 isEmpty() 方法检查栈是否为空。
  • 字符串与字符的比较

    • 使用 == 运算符来比较字符是否相等。
  • 错误处理

    • 返回 false 来处理各种非法情况,例如不匹配的标签、不合法的标签名、字符串结束前没有闭合的标签等。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词