如何在JavaScript中获取字符串的所有组合以及给定的替换?

时间:2019-12-14 17:17:12

标签: javascript string algorithm math permutation

我想返回一个字符串的所有可能组合,同时保持所有内容的正确顺序并避免重复。原因是什么?我想通过允许假名和汉字的混合来更灵活地回答一些日本测验。因此,我需要所有可能的组合来与用户的答案进行比较。

这是该函数的当前语法:(located here

Genki.getAlts('{月曜日}と{水曜日}と{金曜日}に{日本語}のクラスがあります', 'げつようび|すいようび|きんようび|にほんご');

花括号中的文本是将由第二个参数中的替代文本替换的文本,我将其简称为替换。但是,替代文本只能替换相同的索引。那就是:

  • 月曜日只能用げつようび
  • 代替
  • 水曜日只能用すいようび
  • 代替
  • 依此类推...

举一个我想要实现的简单示例。说我有以下内容:

Genki.getAlts('...{A}...{B}...', '1|2', true);

我希望它返回所有组合,如下所示。

'...1...{B}...'
'...1...2...'
'...{A}...2...'
'...{A}...{B}...'

当前实现在2-7个给定的替换下效果很好,但是当给定超过8个时,总组合覆盖率开始下降。可以使用以下公式计算组合的总数:Math.pow(2, 8),它将返回“ 256”组合进行8次替换,但目前getAlts()仅返回234个连击,这意味着我们缺少22个连击,仅给出我们有91%的组合覆盖率。

这就是我目前遇到的问题。您可以通过下面的链接查看当前代码。 (是的,这是相当荒谬的)自学成才后,我竭尽所能获得尽可能多的连击,但我担心我的数学技能并不那么好。我敢肯定,有一个简单得多的方法可以解决这个问题,而我只是想得太清楚了。

以当前算法失败为例,打开控制台,您应该会看到关于最后一个问题的警告,内容如下:

  

234/256(91.40625%的连击覆盖8次替换; 22个连击丢失

此问题的代码:

Genki.getAlts('{1:私}はきのう{学校}で{1:写真}を{1:撮}りました。{2:私}は{家}でも{2:写真}を{2:撮}りました。', 'わたし|がっこう|しゃしん|と|わたし|いえ|しゃしん|と', true);

还有一个简单得多的替代品,其中有10个替代品,可以在控制台中执行测试用例:

Genki.getAlts('{A}{B}{C}{D}{E}{F}{G}{H}{I}{J}', '1|2|3|4|5|6|7|8|9|10', true)

是否有任何可能且简单的方式返回一个字符串的所有组合,而不管指定了多少个替换?虽然我确实知道有多少种组合,但是使用Math.pow(2, n),我不确定如何正确地将它们全部获得。

我愿意听到有关实现此目的的现有算法或框架。

PS:实际上,该算法可用于2-7次替换,几乎没有问题达到或超过此阈值。但是,当他们这样做时,用户的答案可能会被错误地标记为错误,因此我想避免这种情况。最简单的解决方案显然是避免打破7,但这并非总是可能的,此外,我目前实现这一目标的方法并非最佳,因此我也希望对其进行优化。

1 个答案:

答案 0 :(得分:2)

您可以使用二进制数学解决此问题。这是一种生成字符串数组的方法:

function getAlts(str, alt) {
  var subs = alt.split('|');
  var length = subs.length;
  var permutations = Math.pow(2, length);
  var results = [];

  for (var i = 0; i < permutations; ++i) {
    var bitIndex = 0;
    var result = str.replace(/\{(.*?)\}/g, function (match, p1) {
      var subIndex = bitIndex++;
      var bit = length - 1 - subIndex;
      return ((1 << bit) & i) ? subs[subIndex] : p1;
    });

    results.push(result);
  }

  return results;
}

console.log(getAlts('...{A}...{B}...', '1|2'));

或者,如果您能够使用ES6(ECMAScript 2015),则可以编写generator function以使用更少的内存:

function* getAlts(str, alt) {
  var subs = alt.split('|');
  var length = subs.length;
  var permutations = Math.pow(2, length);

  for (var i = 0; i < permutations; ++i) {
    var bitIndex = 0;
    var result = str.replace(/\{(.*?)\}/g, function (match, p1) {
      var subIndex = bitIndex++;
      var bit = length - 1 - subIndex;
      return ((1 << bit) & i) ? subs[subIndex] : p1;
    });

    yield result;
  }
}

var results = getAlts('{A}{B}{C}{D}{E}{F}{G}{H}{I}', '1|2|3|4|5|6|7|8|9');
var total = 0;

for (var result of results) {
  console.log(result);
  total++;
}

console.log('total:', total);