Wildcard字符串分析算法

title: Wildcard字符串分析算法 id: 1de068f425d98e7cc2789a395f8dec94 tags: [] date: 2000/01/01 00:00:00 updated: 2023/03/04 19:29:12 isPublic: true --#|[分隔]|#--

Wildcard字符串分析算法

适用场景

给定一个长字符串,再给定包含*?的正则短字符串,判断这个长字符串是否包含正则短字符串。

算法原理

和KMP算法类似,但需要先把短字符串以 * 为分隔,分成几段。

比如 短字符串:ab*cd*cd,那么只要长字符串的开头两位为 ab,最后两位为ef,中间那部分包含一个cd,就是符合的,比如一下这几个长字符串都是符合的:

abcdcdab0000cdcdab0000cd0000cd

但要注意,长字符串 abcd 是不符合的。

最后就是 ? 的处理,这个比较简单,因为它表示的是有一位任意的字母,哪怕是空格也符合,在对比时多一个判断即可。

代码实现

/**
 * Wildcard字符串分析算法
 * 传入长和短字符串, 返回长字符串是否符合短字符串定义的格式
 * 短字符串中可能包含 * 和 ?
 * @param {String} longStr 
 * @param {String} shortStr 
 */
function Wildcard(longStr, shortStr) {
  let startCount = 0 // * 的个数

  for (let i = 0; i < shortStr.length; i++) {
    if (shortStr[i] === '*') startCount++
  }

  // 没有 *, longStr, shortStr 需要是全等(注意里面有?匹配符)
  if (!startCount) {
    // 长度不同
    if (shortStr.length !== longStr.length) {
      return false
    }
    for (let i = 0; i < shortStr.length; i++) {
      if (longStr[i] !== shortStr[i] && shortStr[i] !== '?') {
        return false
      }
    }
    return true
  } else { // 包含 * , 开始遍历处理

    let i = 0 // 取的 shortStr 的索引值
    let lastIndex = i // 正则的 exec 的 lastIndex, 后面会用

    // 判断第一个 * 之前的字符
    {
      for (; shortStr[i] !== '*'; i++) {
        if (longStr[i] !== shortStr[i] && shortStr[i] !== '?') {
          return false
        }
      }
      lastIndex = i // 更新一下, 让后面校验 longStr 时, 从 i 这个索引开启(i之前的部分,都是第一个星号之前的,已经匹配完了)
    }

    { // 判断中间部分的匹配
      for (let p = 0; p < startCount - 1; p++) {
        ++i // 注意, 经历了上面和上一个循环的步骤 shortStr[i] === '*', 所以 i 需要先加1

        let subshortStr = '' // shortStr 中, 被 * 分隔的字符串片段
        // shortStr 的当前这一项不是 *, 拼接取来, 用来下面的匹配查找
        while (shortStr[i] !== '*' && i < shortStr.length) {
          subshortStr += shortStr[i]
          i++
        }

        let Reg = new RegExp(subshortStr.replace(/\?/g, '[\\s\\S]'), 'g')

        Reg.lastIndex = lastIndex
        // 没有匹配上
        if (!Reg.exec(longStr)) {
          return false
        }

        // 这个片段匹配上了, 更新一下 lastIndex, 给下一个循环备用
        lastIndex = Reg.lastIndex
      }

      { // 判断最后一个 * 后面的字符, 必须全字符匹配

        ++i // 注意, 经历了上面和上一个循环的步骤 shortStr[i] === '*', 所以 i 需要先加1
        // 最后一个 * 后面还有东西
        if (i < shortStr.length) {

          for (let j = 1; shortStr[shortStr.length - j] !== '*'; j++) {
            let longStrIndex = longStr.length - j
            if (longStrIndex < lastIndex) {
              // 和上面已经比过的字符重叠了
              return false
            }
            if (longStr[longStr.length - j] !== shortStr[shortStr.length - j] && shortStr[shortStr.length - j] !== '?') {
              return false
            }
          }
        }
      }
      // 判断完成, 能走到这儿, 匹配通过
      return true
    }
  }
}

console.log(Wildcard('sfsff', 'sf?f'))

Last updated

Was this helpful?