我正在寻找一种算法来减少有序但不唯一的项目的列表(播放列表)。 搜索集合理论但尚未发现任何合适的东西
实施例
[a, b, b, c] -> [a, b, b, c] Cannot be reduced.
[a, b, b, a, b, b] -> [a, b, b].
[b, b, b, b, b] -> [b].
[b, b, b, b, a] -> [b, b, b, b, a] Cannot be reduced.
考虑获取所有现有的子列表并计算每个实例。 如果存在子列表长度等于原始列表的计数次数的子列表,请选择符合此条件的最短子列表。
这似乎有点蛮力,必须有更简单/更快的解决方案。
答案 0 :(得分:3)
对于初学者,您不需要检查所有子列表 - 只需要那些长度为 factor 的完整列表长度的子列表。
如果你主要关注的是简单编码而不是原始速度,那么让正则表达式引擎解决问题:
/^(.+?)\1+$/
答案 1 :(得分:2)
对于每个n <= N
(其中N
是列表的长度),如果n
是N
的因子。如果是,则检查重复第一个n
字符的子列表是否生成原始列表。如果确实如此,那么你找到了一个可能的答案(答案是最短的)。这应该会降低到O(N^2)
以下,但在最坏的情况下仍然与蛮力相同。
你可以做一些修剪,注意如果例如长度为2的子列表成功生成前4个字符而不是完整列表,则长度为4的子列表将失败。你可以将所有这些子列表长度的列表保存到 not 检查,这将减少一些计算。
答案 2 :(得分:0)
使用素数对每个元素进行编码。
例如:
a -> 2
b -> 3
c -> 5
等
现在,您需要再维护两个列表。
第一个列表是素数,第二个列表是指数。
这个想法是;当你偶然发现一个元素时,记录它的素数和它连续出现的次数。
对于[a, b, b, c]
,你得到了这个:
[2, 3, 3, 5]
可记录为:
[2, 3^2, 5]
或更准确地说:
[2^1, 3^2, 5^1]
并保留两个列表:
[2,3,5] // primes in succession - list [p]
[1,2,1] // exponents - list [e]
现在,通过检查第一个元素[p] ^ [e]是否与最后一个元素相同,从末尾到中间迭代这两个列表;如果是,那么倒数第二,依此类推......如果所有这些都相同,你的清单就可以减少。
在此示例中,您检查2^1*5^1 == 3^2*3^2
;因为它不是,所以不能减少。
让我们试试[a, b, b, a, b, b]
:
编码为
[2^1, 3^2, 2^1, 3^2]
,或者
[2, 3, 2, 3] // primes
[1, 2, 1, 2] // exponents
现在,我们检查2^1 * 3^2 == 3^2 * 2^1
(第一个素数,第一个指数乘以最后一个素数,最后一个指数,然后与倒数第二个相比)
由于这一点,它可以减少。
让我们试试[b, b, b, b, b]
:
这可以编码为
[3^5]
,或者
[3] // primes
[5] // exponents
这是一种特殊情况:如果您有1个元素列表,那么您的原始列表可以减少。
让我们试试[b, b, b, b, a]
:
这可以编码为
[3^4, 2^1]
,或者
[3, 2] // primes
[4, 1] // exponents
我们检查是否3^4 == 2^1
,因为它不是,所以您的列表不可缩减。
让我们试试[a, b, a, b, a, b]
:
这可以编码为
[2^1, 3^1, 2^1, 3^1, 2^1, 3^1]
,或者
[2, 3, 2, 3, 2, 3]
[1, 1, 1, 1, 1, 1]
尝试上述过程有效,因为2^1 * 3^1 == 3^1 * 2^1 == 2^1 * 3^1
所以,算法将是这样的:
将所有数字编码为素数。
遍历您的列表,制作两个列表并按照描述填充它们
既然您有两个列表p
和e
,它们都有n
长度:
var start = p[0]^e[0] * p[n-1]^e[n-1]
var reducible = true;
for (int i = 0; i < n/2, ++i) :
if ( (p[i]^e[i] * p[n-i]^e[n-i]) != start ) :
reducible = false;
break;
注意:我没有真正编写此算法的代码,并尝试将其用于各种输入。这只是一个想法。
此外,如果列表可以从n
的长度和长度缩小,那么查看如何将原始列表缩减为基本格式应该不会太难。
第二个注意事项:如果有人发现上述错误,请纠正我。有可能这一切都没有用,因为它已经很晚了,而且我的注意力并不是最佳的。
答案 3 :(得分:0)
这里有一些简单的代码应该在接近线性的时间内运行(最差的是O(n lg lg n)我认为,依赖于更高的数学运算)。
f(x) {
i = 1;
while (i <= size(x) / 2) {
if (size(x) % i != 0) { i++; continue;}
b = true;
for (j = 0; j + i < x.size(); j++) {
if (x[i] != x[j]) {
b = false;
break;
}
}
if (b) return i;
i = max(i + 1, j / i * i / 2); // skip some values of i if j is large enough
}
return -1;
}
基本上,上面执行了朴素算法,但是跳过了一些周期性因素,这些周期性因为早期的“近乎未命中”而已知是不可能的。例如,如果您尝试5周期并看到“aaaabaaaabaaaabaaaabab”,您可以安全地跳过6,7,...,10,因为我们看到4个循环的5次重复然后失败。
最终,你最终做了线性工作量加上一些线性的sigma(n)工作量,n的除数之和,以O(n lg lg n)为界。
*请注意,证明此跳过的正确性非常微妙,我可能在细节上犯了错误 - 欢迎评论。