【问题标题】:String manipulation algorithm to find string greater than original string字符串操作算法来查找大于原始字符串的字符串
【发布时间】:2019-02-15 13:36:42
【问题描述】:

我很少有像'hefg','dhck','dkhc','lmno' 这样的单词(字符串),它们将通过交换部分或全部字符转换为新单词,这样新单词在字典上大于原始单词,而且新单词也是最少的大于原词的词。 例如'dhck' 应该输出 'dhkc' 而不是 'kdhc','dchk' 或任何其他。

我有这些输入

hefg
dhck
dkhc
fedcbabcd

应该输出哪个

hegf
dhkc
hcdk
fedcbabdc

我已经尝试在 python 中使用此代码,它适用于除'dkhc''fedcbabcd' 之外的所有代码。 我发现'fedcbabcd' 的第一个字符是最大值,所以它没有被交换。并且 我得到"ValueError: min() arg is an empty sequence"

我如何修改算法来修复这些情况?

list1=['d','k','h','c']
list2=[]
maxVal=list1.index(max(list1))
for i in range(maxVal):
    temp=list1[maxVal]
    list1[maxVal]=list1[i-1]
    list1[i-1]=temp
    list2.append(''.join(list1))
print(min(list2))

【问题讨论】:

  • 你能解释一下such that the new word is greater than the original吗?
  • @yatu 我认为 OP 希望在字典上比原始单词“大一个”的排列,即所有排列的排序列表中原始单词之后的下一个。
  • 按字典顺序,例如 'acdb' 大于 'abcd'

标签: python string algorithm


【解决方案1】:

你可以试试这样的:

  • 逆序迭代字符串中的字符
  • 跟踪您已经看过的角色,以及您在哪里看到的
  • 如果您看到的字符大于当前字符,请将其换成最小的较大字符
  • 对该位置之后的所有字符进行排序以获得最小字符串

示例代码:

def next_word(word):
    word = list(word)
    seen = {}
    for i in range(len(word)-1, -1, -1):
        if any(x > word[i] for x in seen):
            x = min(x for x in seen if x > word[i])
            word[i], word[seen[x]] = word[seen[x]], word[i]
            return ''.join(word[:i+1] + sorted(word[i+1:]))
        if word[i] not in seen:
            seen[word[i]] = i

for word in ["hefg", "dhck", "dkhc", "fedcbabcd"]:
    print(word, next_word(word))

结果:

hefg hegf
dhck dhkc
dkhc hcdk
fedcbabcd fedcbabdc

【讨论】:

  • 你能解释一下你提到的“最小的大字符”是什么吗?
  • @dutta.ari 嗯,所有已经看到的字符中最小的一个,比当前字符大,如min(x for x in seen if x > word[i])
【解决方案2】:

在一般情况下,最大字符及其位置不会影响算法。例如,对于'fedcbabcd',您可以在字符串的开头添加az,这不会改变您需要交换最后两个字母的事实。

考虑输入'dgfecba'。在这里,输出是'eabcdfg'。为什么?请注意,最后六个字母是按降序排列的,因此通过更改那里的任何内容,您会得到一个较小的字典字符串,这是不好的。因此,您需要替换初始的'd'。我们应该用什么来代替它?我们想要大于'd',但尽可能小,所以'e'。剩下的六个字母呢?同样,我们想要一个尽可能小的字符串,因此我们按字典顺序对字母进行排序:'eabcdfg'

所以算法是:

  • 从字符串的后面开始(右端);
  • 在符号不断增加的同时向左走;
  • i成为s[i] < s[i + 1]的最右边位置;在我们的例子中,这是i = 0;
  • 保持位置 0、1、...、i - 1 上的符号保持不变;
  • 找到i+1 ... n-1中包含大于s[i]的最小符号的位置;打电话给这个职位j;在我们的例子中,j = 3;
  • 交换s[i]s[j];在我们的例子中,我们获得'egfdcba';
  • 反转字符串s[i+1] ... s[n-1];在我们的例子中,我们获得了'eabcdfg'

【讨论】:

    【解决方案3】:

    我们可以将您的问题改写为finding the next lexicographical permutation of a string

    上述链接中的算法描述如下:

    1) 找到最长的非递增后缀

    2) 左边的数字 后缀是我们的支点

    3) 找到枢轴的最右边的继任者 后缀

    4) 交换后继者和枢轴

    5) 后缀颠倒

    上面的算法特别有趣,因为它是O(n)

    代码

    def next_lexicographical(word):
        word = list(word)
    
        # Find the pivot and the successor
        pivot = next(i for i in range(len(word) - 2, -1, -1) if word[i] < word[i+1])
        successor = next(i for i in range(len(word) - 1, pivot, -1) if word[i] > word[pivot])
    
        # Swap the pivot and the successor
        word[pivot], word[successor] = word[successor], word[pivot]
    
        # Reverse the suffix
        word[pivot+1:] = word[-1:pivot:-1]
    
        # Reform the word and return it
        return ''.join(word)
    

    如果单词已经是最后一个字典排列,上述算法将引发StopIteration 异常。

    示例

    words = [
        'hefg',
        'dhck',
        'dkhc',
        'fedcbabcd'
    ]
    
    for word in words:
        print(next_lexicographical(word))
    

    输出

    hegf
    dhkc
    hcdk
    fedcbabdc
    

    【讨论】:

    • 您的第一句话不正确,请参阅 OP 的第三个示例。下一个字典排列是hcdk,而不是'hkdc'
    • @tobias_k 正确,已修复
    猜你喜欢
    • 1970-01-01
    • 2014-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    • 2018-09-26
    • 2018-11-13
    • 2020-07-12
    相关资源
    最近更新 更多