字典最小排列使得所有相邻字母都是不同的

时间:2014-09-04 08:34:04

标签: java arrays algorithm sorting

这是一项奖学金任务,我们还没有接受任何教学,而且我没有找到完整的代码,但一些提示可以很快。我回到家时会发布到目前为止我用Java做的事情,但这里已经有了我已经完成的事情。

所以,我们必须做一个排序算法,例如排序" AAABBB"到ABABAB。最大输入大小为10 ^ 6,所有这些都必须在1秒内完成。如果有多个答案,则按字母顺序排列的第一个答案是正确答案。我开始测试不同的算法,甚至在没有字母顺序要求的情况下对它们进行排序,只是为了看看事情是如何运作的。

第一个版本:

将ascii代码保存到Integer数组中,其中index是ascii代码,值是char数组中该字符出现的数量。 然后我选择了2个最高的数字,并开始将它们互相垃圾邮件发送到新的字符数组,直到某个数字更高,然后我换了它。它运作良好,但当然订单并不正确。

第二版:

遵循相同的想法,但停止选择最常出现的数字,并按照它们在我的数组中的顺序选择索引。运作良好,直到输入像CBAYYY。算法将其分类为ABCYYY而不是AYBYCY。当然,我可以尝试为那些Y的人找到一些免费的地方,但那时候开始花费太长时间。

6 个答案:

答案 0 :(得分:7)

一个有趣的问题,有趣的调整。是的,这是一种排列或重新排列而不是一种排列。不,引用的问题不重复。

算法。

  1. 计算字符频率。
  2. 按字母顺序输出两个最低的字母。
  3. 当每个人都筋疲力尽时,请转到下一个。
  4. 在某些时候,最高频率的char将是其余字符的一半。此时切换到按字母顺序依次与剩余的其他字符交替输出所有字符。
  5. 需要注意避免逐个错误(奇数与偶数个输入字符)。否则,只需编写代码并使其正常工作就是挑战。


    请注意,有一种特殊情况,其中字符数为奇数,一个字符的频率从(半加1)开始。在这种情况下,你需要从算法的第4步开始,依次输出所有一个字符与其他字符交替。

    另请注意,如果一个字符占输入的一半以上,那么对于这种特殊情况,则无法解决问题。可以通过检查频率或在尾部由所有一个字符组成的执行期间预先检测这种情况。检测此案例不属于规范。


    由于不需要排序,因此复杂度为O(n)。每个字符都要检查两次:一次是计数,一次是加到输出。其他一切都是摊销的。

答案 1 :(得分:0)

首先计算数组中每个字母的数量:

例如,你有3 - A,2 - B,1 - C,4 - Y,1 - Z。

1)然后你每次都把最低的一个(它是A),你可以放。

所以你从:

开始

A

然后你不能再放A了所以你把B:

AB

然后:

ABABACYZ

如果您仍然至少有两种字符,则可以使用这些字符。但在这里你仍然会有3个Y.

2)要放置最后一个字符,你只需从你的第一个Y开始,然后在开头的方向上插入一个。(我不知道这些是用英语说的好方法)。

所以ABAYBYAYCYZ。

3)然后你把你的Y之间的后续顺序放在YBYAYCY和你之间的字母Y:

BAC => ABC

你到达

ABAYAYBYCYZ

这应该是你的问题的解决方案。

要做所有这些事情,我认为LinkedList是最好的方法

我希望它有所帮助:)

答案 2 :(得分:0)

我的想法如下。通过正确的实现,它几乎是线性的。

首先建立一个功能来检查解决方案是否可行。它应该非常快。像最常见的字母> 1/2所有字母,如果它可以是第一个,则考虑到它们。

然后,在仍有字母的情况下,按照字母顺序排列的第一个字母与之前的字母不同,并提供进一步的解决方案。

答案 3 :(得分:0)

正确的算法如下:

  1. 构建输入字符串中字符的直方图。
  2. 将CharacterOccurrences放在PriorityQueue / TreeSet中,以最高出现次数,最低字母顺序排序
  3. 有一个CharacterOccurrence类型的辅助变量
  4. PQ不为空时循环

    1. 抓住PQ的头部并保持
    2. 将头部的字符添加到输出
    3. 如果辅助变量设置=>将其重新添加到PQ
    4. 将保留的头部存储在辅助变量中,少发生1次,除非事件最终为0(然后取消设置)
  5. 如果输出的大小==输入的大小,则有可能并且您有答案。否则这是不可能的。

  6. 复杂度为O(N * log(N))

答案 4 :(得分:0)

制作字符频率的双向表:character->countcount->character。记录存储最后一个字符的optional<Character>(或者没有存储任何字符)。存储总字符数。

如果(字符总数-1)<2 *(最高计数字符数),请使用最高计数字符数字符。 (否则就没有解决方案)。如果它是最后一个字符输出则失败。

否则,按字母顺序使用最早的字符输出。

记录最后一个字符输出,减少总字符数和已用字符数。

循环,而我们还有角色。

答案 5 :(得分:0)

虽然this question并不完全重复,但my answer的部分给出了用尽可能少的相邻相等字母枚举所有排列的算法,可以适应只返回最小值,因为它最优性证明要求每个递归调用至少产生一个置换。测试代码之外的更改范围是按排序顺序尝试键,并在找到第一个匹配后中断。 以下代码的运行时间是多项式(如果我更喜欢更好的数据结构,则为O(n)),因为与其祖先不同,它不会列举所有可能性。

david.pfx's回答了逻辑上的暗示:贪婪地接受并没有消除所有可能性的信件,但正如他所指出的,细节是微妙的。

from collections import Counter
from itertools import permutations
from operator import itemgetter
from random import randrange


def get_mode(count):
    return max(count.items(), key=itemgetter(1))[0]


def enum2(prefix, x, count, total, mode):
    prefix.append(x)
    count_x = count[x]
    if count_x == 1:
        del count[x]
    else:
        count[x] = count_x - 1
    yield from enum1(prefix, count, total - 1, mode)
    count[x] = count_x
    del prefix[-1]


def enum1(prefix, count, total, mode):
    if total == 0:
        yield tuple(prefix)
        return
    if count[mode] * 2 - 1 >= total and [mode] != prefix[-1:]:
        yield from enum2(prefix, mode, count, total, mode)
    else:
        defect_okay = not prefix or count[prefix[-1]] * 2 > total
        mode = get_mode(count)
        for x in sorted(count.keys()):
            if defect_okay or [x] != prefix[-1:]:
                yield from enum2(prefix, x, count, total, mode)
                break


def enum(seq):
    count = Counter(seq)
    if count:
        yield from enum1([], count, sum(count.values()), get_mode(count))
    else:
        yield ()


def defects(lst):
    return sum(lst[i - 1] == lst[i] for i in range(1, len(lst)))


def test(lst):
    perms = set(permutations(lst))
    opt = min(map(defects, perms))
    slow = min(perm for perm in perms if defects(perm) == opt)
    fast = list(enum(lst))
    assert len(fast) == 1
    fast = min(fast)
    print(lst, fast, slow)
    assert slow == fast


for r in range(10000):
    test([randrange(3) for i in range(randrange(6))])