js实现查找重复次数最多最长最靠前的子串

801 阅读1分钟

在一次面试中,被问到如何实现查找重复次数最多最长最靠前的子串,面试中,经过自己的一些分析实现了一段代码,后面查阅资料,其实就是 后缀数组查找算法,核心便是 尽可能跳过无效的判断,如abcdabc,其中的d只出现一次,便不需要没有必要跟后面的字符组成去查找了。 算法步骤如下:

  1. 形成后缀数组,如abcdabc,便是['abcdabc', 'bcdabc', 'cdabc', 'dabc', 'abc', 'bc']
  2. 排序,如 ['abc', 'abcdabc', 'bc', 'bcdabc', 'cdabc', 'dabc']
  3. 相邻两个数组对比查找,并记录在map中,如['abc', 'abcdabc'],便形成了map['ab'] = 2, map['abc'] = 2
  4. 遍历map,找出符合条件的。 算法时间,2步骤为O(nlogn), 3最坏为O(2+...+n)即O(n^2/2) 优化点(未代码实现)
  5. compare中,每if (p1.value[0] !== p2.value[0]),便记录以首字母开头的最多次数最长的子串,节省空间。 js实现如下
// 这份代码可直接在chrome的控制台运行
// 比较并存储重复子串数据
function compare (p1, p2, map) {
  if (p1.value[0] !== p2.value[0]) return
  let key = p1.value[0]
  let minLen = Math.min(p1.value.length, p2.value.length)
  for (let i = 1; i < minLen; i ++) {
    if (p1.value[i] === p2.value[i]) {
      key += p1.value[i]
      if (!map[key]) {
        map[key] = {
          count: 1,
          index: Infinity,
        }
      }
      map[key].count ++
      // 最小的下标
      map[key].index = Math.min(p1.index, p2.index)
    } else {
      break
    }
  }
}

function findMaxCountSubString(str) {
  const arr = []
  for (let i = 0, len = str.length; i < len - 1; i ++) {
    arr.push({
      index: i,
      value: str.substr(i),
    })
  }
  arr.sort ((p1, p2) => p1.value < p2.value ? -1 : 1)
  const map = {}
  for (let i = 0, len = arr.length; i < len - 1; i ++) {
    compare(arr[i], arr[i + 1], map)
  }
  let mc = 0 // max count
  let mi = Infinity // min index
  let ms = '' // max length string
  console.log(map)
  Object.keys(map).forEach(key => {
    const value = map[key]
    if (value.count >= mc) {
      mc = value.count
      if (key.length > ms.length) {
        ms = key
        mi = value.index
      } else if (key.length === ms.lengt) {
        if (value.index < mi) {
          ms = key
          mi = value.index
        }
      } else {
        ms = key
        mi = value.index
      }
    }
  })
  console.log('mc:', mc, ';ms:', ms, ';mi', mi)
}

chromeconsole结果如下