【问题标题】:Algorithm to find the sum of all contiguous substrings of a set查找集合的所有连续子串之和的算法
【发布时间】:2017-05-16 10:15:34
【问题描述】:

我正在尝试编写一种高效的算法,可以找到数组所有可能的连续子字符串的总和(保留顺序和组合可以是任意长度)

例如:

[1,2,3,4] -> 1 + 2 + 3 + 4 + 12 + 23 + 34 + 123 + 234 + 1234 = 1670

同样重要的是要注意数组可以重复多次

到目前为止我最好的尝试可能是这样的:(n 是一组数字)

k = 3 // number of times the array repeats
length = len(n)
total = 0

for i in range(0, length*k):
    for exp in range(0, length*k-i): 
    //iterate though all of the possible powers of ten a certain number could be in
    // ie. all the different places that number could be in for all combinations 

        total += ((n[i % length] * 10**exp) * (i + 1))
        // ^ turns number from standard from into int. The i + 1 account for
        // the fact the number could be in the same position in more than one combination

return total

但是,该算法必须针对其中包含超过 10^20 个数字的数组运行,因此我正在寻找一种更快的算法。

注意所有数字都是个位数,数字可以重复

【问题讨论】:

  • 您尝试过 itertools.combinations 吗?
  • @PetarPetrovic 我考虑过,但数组太大而无法使用整个东西 - 我收到内存错误。它重复多次,所以我只使用未重复的部分
  • @PetarPetrovic Ive 更新了我的代码以反映这一点
  • 10^20 个数字?你从哪里得到一台内存超过 100 EB 的计算机?
  • 您的示例没有显示“组合”,它显示了子字符串。

标签: algorithm math numbers mathematical-optimization discrete-mathematics


【解决方案1】:

我们可以通过记录包含该元素的子数组的可能开始(左)和结束(右)位置的数量来计算任何给定元素在任何给定 10 的幂的出现次数。

起始位置的数量只是左侧元素的数量(+1),结束位置的数量只是右侧的元素数量(+1)。例如,[4,5,6,7] 中包含 6 的子数组将有 3 个起始位置和 2 个结束位置:

s s s e e
↓ ↓ ↓ ↓ ↓
[4,5,6,7]

起始位置不会影响元素出现在的 10 次方 - 456566 显示包含元素 6 的子数组的 3 个不同起始位置,但对于所有这些子数组 6位于 100。可能的起始位置的数量将直接乘以元素在某个位置出现的频率(3 个起始位置 -> 每个位置可以出现 3 次)。

结束位置影响元素出现的 10 次方,但不影响出现的次数:556567 显示包含元素 5 的子数组的 3 个结束位置。 5 出现在 100、101 和 102,每次只出现一次。我们可以用5*111 来总结一下。

将这两件事放在一起,对任何元素的总和的影响是:

element * start positions * 111...(end positions times)...11

如上所述,start positions 是 1 + 当前索引(从 0 开始的数组)。并且end positions 在我们从数组的左侧移动到右侧时减 1(或者当我们从右到左移动时增加 1),因此,对于上面最右边的项,我们可以从右边开始1 只需乘以 10 并重复加 1。

这导致一些相当简单的(Java)代码:

int[] array = {1,2,3,4};
int sum = 0;
int endMultiplier = 0;
for (int i = array.length-1; i >= 0; i--)
{
    endMultiplier = 10*endMultiplier + 1;
    sum += array[i] * (i+1) * endMultiplier;
}
System.out.println(sum); // prints 1670

Live demo.


如果元素可以是多位数,上面的方法可以概括为,不是将endMultiplier乘以10,而是根据当前元素的长度(长度为1的10 , 100 表示长度 2 等)。

int endMultiplier = 1;
int sum = 0;
for (int i = array.length-1; i >= 0; i--)
{
    sum += array[i] * (i+1) * endMultiplier;
    // Direct method of calculating k (with floating points):
    // int k = array[i] == 0 ? 10 : (int)Math.pow(10, 1+(int)Math.log10(array[i]));
    int k = 10;
    for (; k < array[i]; k *= 10)
        {}
    endMultiplier = k*endMultiplier + 1;
}

Live demo.

【讨论】:

  • 仅适用于具有单个数字的数组,例如不是 [22, 101, 3]。
  • @maraca 用更通用的方法更新了答案,尽管问题确实表明元素是个位数。
  • 对了,终于看到了。我的阅读理解最近让我失望了。 +1
【解决方案2】:

我终于在 O(n) 中找到了generalized version,它适用于任何长度的正数:

document.body.innerHTML = solve([1,2,3,4]) + " = 1670<br>";
document.body.innerHTML += solve([22,101,3]) + " = " + (22+101+3+22101+1013+221013);

function solve(arr) {
    let n = arr.length, total = 0, sum = 0;
    for (let i = n - 1; i >= 0; i--) {
        total += arr[i] * (i + 1) * (sum + 1);
        let f = Math.pow(10, len(arr[i]));
        sum = f + sum * f;
    }
    return total;
}

function len(x) {
    if (x < 10)
        return 1;
    return Math.floor(Math.log10(x)) + 1;
}

我通过写出n-1n-3 的方程式得出了这个公式:

n-1: a[n-1] * n
n-2: a[n-2] * (n-1) + a[n-2] * (n-1) * 10^len(a[n-1])
n-3: a[n-3] * (n-2) + a[n-3] * (n-2) * (10^len(a[n-2]) + 10^(len(a[n-1]) + len(a[n-2])))

第一项表示数字在最后一个位置的次数。我们将10^... 部分的总和表示为sum 并分解出a[i] * (i+1),这就是+1*(sum+1) 中的来源。下一次迭代的sum10^len(a[i])+10^len(a[i])*sum。这是因为
10^a + 10^a * (10^b + 10^(b+c) + ...) = 10^a + 10^(a+b) + 10^(a+b+c) + ...

【讨论】:

    【解决方案3】:

    请注意,您可以:
    1) 每次 i-iteration 查找一次 n[i] * (i + 1)
    2) 找到1+10 + 100 + ...10^(length-i-1) 的总和作为算术级数的总和

      s = 10^(length-i) - 1 / 9
      9999..99/9=1111..11
    

    所以你可以获得 O(n) 复杂度。

    3) 更多优化 - 使 1111... 乘数与每次 i 迭代的单个操作(整数除以 10 或使 m*10+1 用于反向循环)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-05
      • 1970-01-01
      • 2020-12-02
      • 1970-01-01
      • 2015-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多