如何生成包含所有可能排列的最短序列?
实施例: 对于长度2,答案是121,因为该列表包含12和21,这些都是可能的排列。
对于长度3,答案是123121321,因为此列表包含所有可能的排列: 123,231,312,121(无效),213,132,321。
每个数字(在给定的排列中)可能只出现一次。
答案 0 :(得分:6)
这种贪心算法产生相当短的最小序列。
更新:请注意for n ≥ 6, this algorithm does not produce the shortest possible string!
好奇的打破平局步骤对于正确性是必要的;随意打破领带似乎导致更长的字符串。
我验证了(通过编写更长,更慢的程序)这个算法给出的长度为4,123412314231243121342132413214321的答案确实是最短的答案。但是,对于长度为6,它会产生长度为873的答案,该答案比最短的已知解决方案更长。
算法为O( n ! 2 )。
Python中的实现:
import itertools
def costToAdd(a, b):
for i in range(1, len(b)):
if a.endswith(b[:-i]):
return i
return len(b)
def stringContainingAllPermutationsOf(s):
perms = set(''.join(tpl) for tpl in itertools.permutations(s))
perms.remove(s)
a = s
while perms:
cost, next = min((costToAdd(a, x), x) for x in perms)
perms.remove(next)
a += next[-cost:]
return a
此函数生成的字符串长度为1,3,9,33,153,873,5913,......似乎为this integer sequence。
我有预感你可以做得比O更好( n ! 2 )。
答案 1 :(得分:5)
n-1
个数字(来自的来源)
结束,并为目标
结束),两个如果他们共享n-2
个数字
等等。 n
的最短路径
顶点。答案 2 :(得分:3)
这是一种快速算法,可生成包含所有排列的短字符串。我很确定它会产生最短的答案,但我手头没有完整的证据。
解释。以下是所有排列的树。图片不完整;想象这棵树永远向右走。
1 --+-- 12 --+-- 123 ...
| |
| +-- 231 ...
| |
| +-- 312 ...
|
+-- 21 --+-- 213 ...
|
+-- 132 ...
|
+-- 321 ...
此树级别 k 的节点都是长度的排列 ķ。此外,排列按特定顺序排列很多 每个排列与其上下邻居之间的重叠。
准确地说,只需添加下一个节点即可找到每个节点的第一个子节点 符号到底。例如,213的第一个孩子将是2134.其余的 通过旋转到第一个孩子以留下一个符号来找到孩子 一时间旋转2134将产生1342,3421,4213。
将所有节点放在给定的级别并将它们串在一起,重叠 尽可能地产生字符串1,121,123121321等
该序列中 n 字符串的长度为the sum for x=1 to n of x!。 (您可以通过观察相邻排列之间存在多少非重叠来证明这一点。兄弟姐妹在除1个符号之外的所有符号中重叠;除了2个符号外,所有兄弟姐妹都重叠;等等。)
证明草图。我还没有完全证明这是最好的解决方案,但这里是一个如何进行证明的草图。首先显示包含 n 不同排列的任何字符串的长度≥2
算法。鉴于所有这些背景,算法非常简单。将此树行走到所需的深度,并将该深度处的所有节点串在一起。
幸运的是,我们实际上不必在内存中构建树。
def build(node, s):
"""String together all descendants of the given node at the target depth."""
d = len(node) # depth of this node. depth of "213" is 3.
n = len(s) # target depth
if d == n - 1:
return node + s[n - 1] + node # children of 213 join to make "2134213"
else:
c0 = node + s[d] # first child node
children = [c0[i:] + c0[:i] for i in range(d + 1)] # all child nodes
strings = [build(c, s) for c in children] # recurse to the desired depth
for j in range(1, d + 1):
strings[j] = strings[j][d:] # cut off overlap with previous sibling
return ''.join(strings) # join what's left
def stringContainingAllPermutationsOf(s):
return build(s[:1], s)
性能。上面的代码已经比我的其他解决方案快得多,并且它可以对大量字符串进行大量剪切和粘贴,您可以对其进行优化。该算法可以在时间和内存中运行,与输出的大小成比例。
答案 3 :(得分:0)
对于n 3个长度链为8 12312132 在我看来,我们正在使用循环系统-换句话说,它正在响。但是我们正在处理环,好像它是链一样。连锁确实是123121321 = 9 但是戒指是12312132 = 8 从序列 1 2312132 [1]的开头开始,我们取321的最后1个。
答案 4 :(得分:0)
这些被称为(最小长度)superpermutations (cf. Wikipedia)。 当匿名用户在4chan上发布新的下限时,对此的兴趣重新燃起。 (有关历史记录,请参见Wikipedia和许多其他网页。)
AFAIK,截止到今天,我们只知道:
我编写了一个非常短的PARI / GP程序(您可以粘贴以运行它on the PARI/GP web site),该程序实现了标准算法,该算法产生长度为A007489(n)的回文超置换:
extend(S,n=vecmax(s))={ my(t); concat([
if(#Set(s)<n, [], /* discard if not a permutation */
s=concat([s, n+1, s]); /* Now merge with preceding segment: */
forstep(i=min(#s, #t)-1, 0, -1,
if(s[1..1+i]==t[#t-i..#t], s=s[2+i..-1]; break));
t=s /* store as previous for next */
)/*endif*/
| s <- [ S[i+1..i+n] | i <- [0..#S-n] ]])
}
SSP=vector(6, n, s=if(n>1, extend(s), [1])); // gives the first 6, the 6th being non-minimal
我认为这很容易翻译成任何其他语言。 (对于非PARI语言用户:“ | x <-”表示“ for x in”。)