在一次面试中,被问到如何实现查找重复次数最多最长最靠前的子串,面试中,经过自己的一些分析实现了一段代码,后面查阅资料,其实就是
后缀数组查找算法,核心便是 尽可能跳过无效的判断,如abcdabc,其中的d只出现一次,便不需要没有必要跟后面的字符组成去查找了。
算法步骤如下:
- 形成后缀数组,如
abcdabc,便是['abcdabc', 'bcdabc', 'cdabc', 'dabc', 'abc', 'bc'] - 排序,如
['abc', 'abcdabc', 'bc', 'bcdabc', 'cdabc', 'dabc'] - 相邻两个数组对比查找,并记录在map中,如
['abc', 'abcdabc'],便形成了map['ab'] = 2, map['abc'] = 2 - 遍历
map,找出符合条件的。 算法时间,2步骤为O(nlogn), 3最坏为O(2+...+n)即O(n^2/2) 优化点(未代码实现) - 在
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)
}
在chrome的console结果如下
