我试着编写一个时间有效的算法,可以找到数组中所有可能的连续子串的总和(保留顺序和组合可以是任意长度)
例如:
[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个数字的数组,所以我正在寻找更快的算法。
请注意,所有数字都是单个数字,数字可以重复
答案 0 :(得分:2)
我们可以通过记录包含该元素的子数组的可能开始(左)和结束(右)位置的数量来计算任何给定幂10的任何给定元素的出现次数。
起始位置的数量只是左边的元素数量(+1),结束位置的数量只是右边元素的数量(+1)。例如,6
中包含[4,5,6,7]
的子数组将包含3个起始位置和2个结束位置:
s s s e e
↓ ↓ ↓ ↓ ↓
[4,5,6,7]
起始位置不会影响元素的显示效果 - 456
,56
和6
显示包含元素6
的子数组的3个不同起始位置,但是对于他们所有人6
都是10 0 。可能的起始位置的数量将是元素在某个位置出现的频率的直接乘数(3个起始位置 - >可以在每个位置出现3次)。
结尾位置会影响元素出现在10的幂,但不会影响其出现的次数:5
,56
和567
显示包含该元素的子数组的3个结束位置元素5
。 5
出现在10 0 ,10 1 和10 2 ,每次只出现一次。我们可以使用5*111
对此进行总结。
将这两件事放在一起,对任何元素的总和的影响是:
element * start positions * 111...(end positions times)...11
如上所述,start positions
是1 +当前索引(基于0的数组)。当我们从阵列的左侧移动到右侧时,end positions
减少1(或者当我们从右向左移动时,1
减少1),因此,对于上面最右侧的术语,我们可以从右边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
,然后乘以10并重复加1。
这导致了一些相当简单的(Java)代码:
endMultiplier
如果元素可以是多个数字,则可以推广上述方法,而不是将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;
}
乘以10,我们会根据当前元素的长度将其相乘(10长度1,100长度2,等等。)
select column2
from tbl1
where column1 in ('A','B','C','D')
order by column2 offset 4 rows fetch next 4 rows only
答案 1 :(得分:1)
请注意,您可以:
1)每次迭代查找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...
乘数每次迭代单次运算(整数除以初始值10或反转为m*10+1
)
答案 2 :(得分:1)
我终于找到了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-1
的等式写出来n-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)
的地方。下一次迭代的sum
为10^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) + ...
。