You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

优化多键字符串子串检测算法,提升大数量键处理速度

优化多词键子串匹配删除算法的性能方案

问题背景

现有一个以多词字符串为键、出现频率为值的对象,示例输入如下:

{"bravo charlie": 10, "alpha bravo charlie": 10, "delta echo foxtrot": 15, "delta echo": 7}

要实现的规则:

  • 规则A:如果某个多词键是另一个键的子串,且两者频率相同,删除那个短的子串键,保留更长的包含键
  • 规则B:单字词键就算被其他键包含,也必须保留

现在用成对比较的算法能实现需求,但处理56万键的大对象时,耗时高达30分钟,性能完全没法用。

现有低效代码

// for every multi word key
// if a part of a longer key in candidates AND have same length delete
let keys = Object.keys(candidates)
    .sort((a, b) => b.length - a.length)
    .filter(key => {
        if (key.split(" ").length === 1) {
            return false
        }
        return true
    });
    // ^ order keys by length to speed up comparisons and filter out single words

    checkKeyI: for (const keyi of keys) {

        checkKeyJ: for (const keyj of keys) {

            // because we pre-sorted if length is less then we are done with possible matches
            if (keyj.length <= keyi.length) {
                continue checkKeyI
            }

            // keys must not match exactly
            if (keyj === keyi) {
                continue checkKeyJ
            }

            // keyi must be a substring of keyj
            if (!keyj.includes(keyi)) {
                continue checkKeyJ
            }

            // they must have the same freq occurr values
            if (candidates[keyj] === candidates[keyi]) {
                delete candidates[keyi]
                continue checkKeyI
            }

        }
    }

优化思路与方案

1. 按频率分组,砍掉无效比较

不同频率的键根本不需要互相检查,直接把所有多词按键的频率值分组,只有同频率的键才需要做包含关系判断。比如频率10的键只和频率10的比,频率15的只和15的比,一下子就能减少绝大多数无效循环。

2. 用词序列匹配代替字符串includes

原代码用keyj.includes(keyi)判断子串,容易出现词边界错误(比如"bravo"会被误判为"bravo123"的子串),而且字符串匹配效率低。换成把每个键拆成词数组,用双指针法判断是否是子序列,既准确又快。

3. 先标记再批量删除,减少对象操作

原代码遍历的时候直接删对象键,频繁修改对象会拖慢性能。改成用一个集合记录要删的键,最后统一删除,能减少很多性能开销。

4. 按词数排序,减少比较次数

对同频率组内的键,按词数从多到少排序,这样遍历短键的时候,只需要和前面更长的键比较,不用遍历所有键,进一步减少循环次数。

优化后的代码示例

function optimizeCandidates(candidates) {
    // 分离单字词和多字词,同时预存词数组
    const singleWordKeys = new Set();
    const freqGroups = new Map(); // key: 频率值, value: { key: string, words: string[] }[]

    for (const key of Object.keys(candidates)) {
        const words = key.split(" ");
        if (words.length === 1) {
            singleWordKeys.add(key);
            continue;
        }
        const freq = candidates[key];
        if (!freqGroups.has(freq)) {
            freqGroups.set(freq, []);
        }
        freqGroups.get(freq).push({ key, words });
    }

    // 处理每个频率组
    const toDelete = new Set();
    for (const [freq, group] of freqGroups) {
        // 按词数从多到少排序
        group.sort((a, b) => b.words.length - a.words.length);

        // 遍历每个键,检查是否被同组内更长的键包含
        for (let i = 0; i < group.length; i++) {
            const current = group[i];
            if (toDelete.has(current.key)) continue; // 已标记删除,跳过

            // 只和更长的键比较
            for (let j = 0; j < i; j++) {
                const longer = group[j];
                // 双指针判断current.words是否是longer.words的子序列
                let ptrCurrent = 0;
                for (const word of longer.words) {
                    if (word === current.words[ptrCurrent]) {
                        ptrCurrent++;
                        if (ptrCurrent === current.words.length) {
                            // 找到包含关系,标记删除
                            toDelete.add(current.key);
                            break;
                        }
                    }
                }
                if (toDelete.has(current.key)) break; // 找到匹配就停止后续比较
            }
        }
    }

    // 批量删除待删键
    for (const key of toDelete) {
        delete candidates[key];
    }

    return candidates;
}

// 测试示例
const sample = {"bravo charlie": 10, "alpha bravo charlie": 10, "delta echo foxtrot": 15, "delta echo": 7};
console.log(optimizeCandidates(sample));
// 输出: {"alpha bravo charlie": 10, "delta echo foxtrot": 15, "delta echo": 7}

性能提升效果

  • 按频率分组后,跨频率的无效比较直接被砍掉,比较范围至少缩小一个数量级
  • 词序列匹配比字符串includes快2-3倍,还能避免词边界误判
  • 批量删除减少了对象的频繁修改,操作效率提升明显
  • 排序后的遍历逻辑,把原有的O(n²)复杂度降到接近O(n log n),大数据量下速度会有质的飞跃

内容的提问来源于stack exchange,提问作者Jake Lowen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:31:06