0502 | 591. 标签验证器

0502 | 591. 标签验证器

正则:

1. 正则中 .*? 是什么意思?
  • . : 匹配 1 个字符,匹配 任何 单个字符;
  • * :匹配 ≥ 0 个字符,匹配 在它之前的那个 字符。
  • ?:匹配 0|1 个字符,匹配 在它之前的那个 字符。

所以:

  • .*:匹配任何字符 ≥ 0 个;
  • .?:匹配任何字符 0|1 个;

那么:

.*? : 匹配表示匹配任意字符到下一个符合条件的字符。

  • 例子:正则表达式a.*?xxx 可以匹配 abxxx axxxxx abbbbbxxx

.*:尽可能匹配符合条件的字符,尽可能长,贪婪模式

例子:

  • 字符串:<img src="test.jpg" width="60px" height="80px"/> 如果要匹配 src 的属性值;
  • src=".*", 匹配结果是:src="test.jpg" width="60px" height="80px"
    • 贪婪模式:意思是从 =" 往后匹配,直到最后一个 ",匹配结束。
  • src=".*?",匹配结果是:src="test.jpg"
    • 懒惰模式:匹配到第一个 " 就结束了一次匹配,不会继续向后匹配。
2. 正则中 [A-Z]{1,9} 是什么意思?
  • 正则中的方括号 [] 表示集合,也就是说只要字符串内容是 A-Z 中的任意大写字母都可以。

  • 正则中的大括号 {} 表示次数,也就是匹配 {} 的前一个字符的次数:

    - {x} // x次
    
    - {min, max} // 介于 min 次到 max 次之间
    
    - {min, } // 至少 min 次
    
    - {0, max} // 至多 max 次
    
    -// 0|1 次
    - *  // ≥ 0 次, 也就是不限制次数,尽可能匹配最长
    - +  // ≥ 1 次
    
    • /a{3}/ 表示匹配 a 3次

所以:原题目中的意思是匹配一个字符串,长度是 1-9 (含9) 个字符,这些字符必须是大写字母。

方法一:使用正则
var isValid = function (code) {
  // cdata 内容
  let reg1 = new RegExp(/<!\[CDATA\[.*?\]\]>|t/g);
  // 开始、结束标签 \1 是一个回溯引用,引用了开始标签`<...>`的内容。
  let reg2 = new RegExp(/<([A-Z]{1,9})>[^<]*<\/\1>/);
  // 匹配cdata,然后置换为 - ;
  code = code.replace(reg1, "-");
  let temp = '';
  while (temp != code) {
    temp = code;
    code = code.replace(reg2, "t");
    console.log(code);
  }
  return code == 't';
};
方法二:使用状态机
  • leetcode解析:链接.
  • 栈 + 字符串遍历
var isValid = function (code) {
  const n = code.length;
  const tags = [];

  let i = 0;
  while (i < n) {
    // 开始标签、结束标签、CDATA开头
    if (code[i] === '<') {
      if (i === n - 1) {
        return false;
      }
      // 结束标签
      if (code[i + 1] === '/') {
        const j = code.indexOf('>', i);
        if (j < 0) {
          return false;
        }
        const tagname = code.slice(i + 2, j);
        // 找到结束标签,让栈中的开始标签出栈,比对是否相等。
        if (tags.length === 0 || tags[tags.length - 1] !== tagname) {
          return false;
        }
        tags.pop();
        i = j + 1;
        if (tags.length === 0 && i !== n) {
          return false;
        }
        // CDATA 内容
      } else if (code[i + 1] === '!') {
        if (tags.length === 0) {
          return false;
        }
        if (i + 9 > n) {
          return false;
        }
        const cdata = code.slice(i + 2, i + 9);
        if ("[CDATA[" !== cdata) {
          return false;
        }
        const j = code.indexOf("]]>", i);
        if (j < 0) {
          return false;
        }
        i = j + 1;
        // 开始标签
      } else {
        const j = code.indexOf('>', i);
        if (j < 0) {
          return false;
        }
        const tagname = code.slice(i + 1, j);
        // 判断开始标签是否符合长度 1-9 要求
        if (tagname.length < 1 || tagname.length > 9) {
          return false;
        }
        // 判断开始标签是否符合字母 A-Z 要求
        for (let k = 0; k < tagname.length; ++k) {
          if (!(tagname[k] >= 'A' && tagname[k] <= 'Z')) {
            return false;
          }
        }
        // 开始标签符合要求,写入 stack 中。
        tags.push(tagname);
        i = j + 1;
      }
    } else {
      // 如果没有 '<' 则只有两种情况:
      // 1. 处在一个开始标签內,且不在CDATA内:'<HTML> ......',这时候让 i++;
      // 2. 不在一个开始标签内,'....<HTML>aaa</HTML>',这时候不符合标准,返回false。通过判断 tags.length 是否为0可以知道。
      if (tags.length === 0) {
        return false;
      }
      ++i;
    }
  }

  return tags.length === 0;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值