【问题标题】:Find optimal set of substrings in given string在给定字符串中找到最佳子字符串集
【发布时间】:2017-11-23 13:15:18
【问题描述】:

我正在尝试找到给定字符串的最佳字符串集。

给定字符串:“FEEJEEDAI”

子字符串值:

FE - 1
乙脑 - 2
JEE - 3
人工智能 - 4
DAI - 6

可能的组合:

1) [FE-JE-DAI] - 1+2+6 = 9
2) [FE-JEE-DAI] - 1+3+6 = 10
3) [FE-JE-AI] - 1+3+4 = 8

最佳组合 - 2) [FE-JEE-DAI] 得分 10

我认为应该是这样的:

1) 检查字符串是否包含特定的子字符串:

var string = "FEEJEEDAI", substring = "JE"; string.indexOf(substring) !== -1;

2) 如果为真,则找到它的索引

var subStringIndex = string.indexOf(substring)

3) 创建新的 tempString 以构建组合并从 string '切断'substring

var tempString = string.slice(subStringIndex, substring.length)

4) 遍历string 并找到最优tempString

我不知道如何将其构建成循环并处理 JE vs JEE、AI vs DAI 的情况

【问题讨论】:

  • 同一个子串能找到多次吗?两个子串可以重叠吗?
  • 是的,可以多次找到子字符串,是的,它们相互重叠——我猜这是最难的部分。
  • 那么如果你在字符串“EEEEEE”中搜索“EE” - 2 和“EEE” - 3 那么结果是什么?
  • 所有可能的组合,所以:EE-EE-EE, EEE-EEE, EE-EEE, EEE-EE 现在算法应该根据添加到子字符串的值选择最佳选项
  • 但是重叠呢?难道也不能是 EE-EE-EE-EE-EE 吗?

标签: javascript string algorithm substring mathematical-optimization


【解决方案1】:

基本上,您可以使用迭代和递归的方法来获取字符串的所有可能子字符串。

此解决方案分为 3 个部分

  1. 准备工作
  2. 收集零件
  3. 计算分数并创建结果集

准备

开始时,字符串的所有子字符串都收集在indices 对象中。键是索引,值是一个有限制的对象,它是模式数组中字符串的最小长度。模式数组包含索引和从该索引开始找到的子字符串。

indices 来自第一个示例的对象

{
    0: {
        limit: 2,
        pattern: [
            {
                index: 0,
                string: "FE"
            }
        ]
    },
    3: {
        limit: 2,
        pattern: [
            {
                index: 3,
                string: "JE"
            },
            {
                index: 3,
                string: "JEE"
            }
        ]
    },
    /* ... */
}

收集零件

主要思想是从索引零开始,使用一个空数组来收集子字符串。

要检查哪些部分在一个组中,您需要获取给定索引处的第一个子字符串或下一个接近的子字符串,然后获取 limit 属性,即最短子字符串的长度,添加索引和将其作为搜索组成员的最大索引。

在第二个示例中,第一组由'FE''EE''EEJ' 组成

string      comment
----------  -------------------------------------
01 2345678  indices
FE|EJEEDAI  
FE|         matching pattern FE  at position 0
 E|E        matching pattern EE  at position 1
 E|EJ       matching pattern EEJ at position 1
^^          all starting substrings are in the same group

使用该组,调用一个新的递归,具有调整后的索引和连接到部件数组的子字符串。

计算分数并创建结果集

如果没有找到更多的子字符串,则连接部分并计算分数并将其推送到结果集。

解释结果

 [
    {
        parts: "0|FE|3|JE|6|DAI",
        score: 9
    },
    /* ... */
]

parts 是该位置的索引和匹配字符串的组合

 0|FE|3|JE|6|DAI
 ^ ^^            at index 0 found FE
      ^ ^^       at index 3 found JE
           ^ ^^^ at index 6 found DAI

score 使用给定的子字符串权重计算

substring  weight
---------  ------
 FE            1
 JE            2
 DAI           6
---------  ------
score          9

示例三返回 11 个唯一组合。

function getParts(string, weights) {

    function collectParts(index, parts) {
        var group, limit;
        while (index < string.length && !indices[index]) {
            index++;
        }
        if (indices[index]) {
            group = indices[index].pattern;
            limit = index + indices[index].limit;
            while (++index < limit) {
                if (indices[index]) {
                    group = group.concat(indices[index].pattern);
                }
            }
            group.forEach(function (o) {
                collectParts(o.index + o.string.length, parts.concat(o.index, o.string));
            });
            return;
        }
        result.push({
            parts: parts.join('|'),
            score: parts.reduce(function (score, part) { return score + (weights[part] || 0); }, 0)
        });
    }

    var indices = {},
        pattern,
        result = [];

    Object.keys(weights).forEach(function (k) {
        var p = string.indexOf(k);
        while (p !== -1) {
            pattern = { index: p, string: k };
            if (indices[p]) {
                indices[p].pattern.push(pattern);
                if (indices[p].limit > k.length) {
                    indices[p].limit = k.length;
                }
            } else {
                indices[p] = { limit: k.length, pattern: [pattern] };
            }
            p = string.indexOf(k, p + 1);
        }
    });
    collectParts(0, []);
    return result;
}

console.log(getParts("FEEJEEDAI", { FE: 1, JE: 2, JEE: 3, AI: 4, DAI: 6 }));
console.log(getParts("FEEJEEDAI", { FE: 1, JE: 2, JEE: 3, AI: 4, DAI: 6, EEJ: 5, EJE: 3, EE: 1 }));
console.log(getParts("EEEEEE", { EE: 2, EEE: 3 }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 这是一个很好的解决方案。如果在预处理阶段您还记录每个索引的最小长度,也许这将是一种优化,因此您不必在递归算法期间进行查找,这可能会在递归树中的替代分支期间重复。跨度>
  • @trincot,好主意,现在限制了最小长度模式。
【解决方案2】:

如果您在找到子字符串时对其进行切片,因为某些子字符串是其他子字符串的子字符串,请先搜索最大的子字符串。例如,如果你没有找到 DAI,而你找到了 AI,它就不能成为 DAI 的一部分。您想测试每个子字符串,因此您可以将每个子字符串放入一个数组并循环遍历该数组。

【讨论】:

    猜你喜欢
    • 2012-05-04
    • 2016-03-26
    • 2011-01-28
    • 1970-01-01
    • 2023-01-19
    • 1970-01-01
    • 2021-04-14
    • 2019-03-03
    相关资源
    最近更新 更多