面试准备:优化swapLexOrder

时间:2017-08-26 17:56:40

标签: python string lexicographic-ordering

在代码争夺中采访哈希映射问题,需要帮助优化我的强力解决方案。这是问题所在:

给定一个字符串str和一对数组,指示字符串中的哪些索引可以交换,返回按字典顺序排列的最大字符串,该字符串是通过执行允许的交换而产生的。你可以多次交换指数。

实施例

For str = "abdc" and pairs = [[1, 4], [3, 4]], the output should be
swapLexOrder(str, pairs) = "dbca".

通过交换给定的索引,您将获得字符串:“cbda”,“cbad”,“dbac”,“dbca”。此列表中按字典顺序排列的最大字符串是“dbca”。

我目前的解决方案

通过不断添加所有可能性直到没有新的解决方案来蛮力。这对swapLexOrder('dznsxamwoj',[[1,2],[3,4],[6,5],[8,10]])来说太慢了,在我的机器上没有完成。任何优化提示?更简单的测试用例是swapLexOrder('abdc,[[1,4],[3,4]])= dbca

def swapLexOrder(str, pairs):
    d = {}
    d[str]=True
    while True:
        oldlen=len(d)
        for x,y in pairs:
            for s in d.keys():
                d[swp(s,x,y)]=True
        if len(d) == oldlen:
            #no more new combinations.
            return sorted(d)[-1]

def swp(str,x,y):
    x=x-1
    y=y-1
    a=str[x]
    b=str[y]
    return str[0:x]+b+str[x+1:y]+a+str[y+1:]

3 个答案:

答案 0 :(得分:3)

我建议的解决方案是首先尝试“链接”尽可能多的对,以形成可以互换的索引集 - 例如,在您的第一个示例中,[[1, 4], [3, 4]]可以变为[[1, 3, 4]]。然后可以对这些索引子集中的每一个进行字典排序以形成输出。实现到此:

def build_permitted_subs(pairs):
    perm = []

    for a, b in pairs:
        merged = False
        for ind, sub_perm in enumerate(perm):
            if a in sub_perm or b in sub_perm:
                sub_perm.add(a)
                sub_perm.add(b)
                merged = True
                break

        else:
            perm.append(set([a, b]))

        if merged:
            for merge_perm_ind in reversed(range(ind + 1, len(perm))):
                if perm[merge_perm_ind] & sub_perm:
                    sub_perm.update(perm[merge_perm_ind])
                    perm.pop(merge_perm_ind)

    return list(map(sorted, perm))

def swap_lex_order(swap_str, _pairs):

    pairs = [[a - 1, b - 1] for a, b in _pairs]
    out = list(swap_str)

    perm = build_permitted_subs(pairs)

    for sub_perm in perm:
        sorted_subset = sorted(sub_perm, key=lambda ind: swap_str[ind], reverse=True)

        for sort, targ in zip(sorted_subset, sub_perm):
            out[targ] = swap_str[sort]

    return "".join(out)

print(swap_lex_order("dznsxamwoj", [[1, 2], [3, 4], [6, 5], [8, 10]]))
print(swap_lex_order("abdc", [[1, 4], [3, 4]]))
print(swap_lex_order("acxrabdz",[[1,3], [6,8], [3,8], [2,7]]))

带输出:

zdsnxamwoj
dbca
zdxrabca

我还重命名了你的参数,不使用str,这已经是一个非常基础的Python内置。请注意,我的代码可能不像Pythonic那样,但我认为它足以说明算法,并且它没有遭受任何重大性能命中。我怀疑这种方法具有相当低的复杂性 - 它通常是“智能的”,因为它不会暴力破坏任何东西,并使用O(n log n)种类。第一个例子似乎是正确的。请注意,这会将每对转换为0,因为这对Python来说要容易得多。

这依赖于能够从相邻排列(交换对)形成任何排列(对链接对进行排序)。这可能不是完全直观的,但它可能有助于实现您可以仅使用列表中的相邻交换来有效地执行插入(通过在方向上不断地交换元素)。使用相邻交换置换列表的示例是冒泡排序,您可能会意识到如果任何排列都可以被起泡,这意味着bubblesort可以达到所有排列。

如果您有任何问题或任何不起作用,请告诉我,我将开始详细说明/调试。 (截至格林尼治标准时间19:28,我已经注意到一个错误并将其编辑出来:)。错误#2(测试用例3中的重复z)也应该修复。

关于bug#1的更多内容:

我没有对build_permitted_subs返回的索引进行排序,因此无法参考swap_str对其进行排序。

关于bug#2的更多信息:

build_permitted_subs功能无法正常工作 - 具体来说,如果它遇到一对可以分为两组,意味着这些组也应该加在一起,这没有发生,现在会有两个不应该分开的群体。这导致z重复,因为两个组都可以从z中绘制。我已经用一面旗帜和一个追溯循环来解决这个问题。

答案 1 :(得分:0)

这个可能效果更好。

def swapLexOrder(str_, pairs):
n = len(str_)
str_ = list(str_)

corr = [set() for _ in range(n)]
nodes = set()
for a, b in pairs:
    corr[a-1].add(b-1)
    corr[b-1].add(a-1)
    nodes.add(a-1)
    nodes.add(b-1)

while nodes:
    active = {nodes.pop()}
    group = set()
    while active:
        group |= active
        nodes -= active
        active = {y for x in active for y in corr[x] if y in nodes}

    chars = iter(sorted((str_[i] for i in group), reverse=True))
    for i in sorted(group):
        str_[i] = next(chars)

return "".join(str_)

答案 2 :(得分:0)

def swapLexOrder(str, pairs):
    
    if not str or not pairs:
        return ('', str)[not pairs]
    lst = [''] + list(str)
    setted_pairs = list(map(set, pairs))
    while setted_pairs:
        path = setted_pairs.pop(0)
        while True:
            path1 = path.copy()
            for pair in setted_pairs:
                if path1 & pair:
                    path |= pair
                    setted_pairs.remove(pair)
            if path == path1:
                break
        optimal = sorted(lst[i] for i in path)
        for i, v in enumerate(sorted(path, reverse=True)):
            lst[v] = optimal[i]
    return ''.join(lst[1:])