【问题标题】:How many minimum numbers of characters from a given string S, should delete to make it a sorted string [duplicate]给定字符串 S 中应删除多少个最小字符以使其成为排序字符串 [重复]
【发布时间】:2017-02-09 22:47:24
【问题描述】:

我需要找到使字符串排序所需的最小删除次数。

示例测试用例:

# Given Input:
teststr = "abcb"
# Expected output:
1

# Explanation
# In this test case, if I delete last 'b' from "abcb", 
# then the remaining string "abc" is sorted. 
# That is, a single deletion is required.

# Given Input:
teststr = "vwzyx"
# Expected output:
2

# Explanation
# Here, if I delete 'z' and 'x' from "vwzyx", 
# then the remaining string "vwy" is a sorted string.  

我尝试了以下方法,但它给出了超出时间限制的错误。 有其他方法解决这个问题吗?

    string = input()
    prev_ord = ord(string[0])
    deletion = 0
    for char in string[1:]:
        if ord(char) > prev_ord +1 or ord(char) < prev_ord:
            deletion += 1
            continue
        prev_ord = ord(char)
    print(deletion)

【问题讨论】:

标签: python string sorting


【解决方案1】:

您当前的算法会为许多字符串提供不正确的结果。

我怀疑有更有效的方法来解决这个问题,但这里有一个蛮力解决方案。它生成输入字符串的子集,按长度降序排列。子集中的元素保留原始字符串的顺序。一旦count_deletions 找到一个有序子集,它就会返回它(转换回字符串),以及删除的数量。因此,它找到的解决方案保证不会比输入字符串的任何其他排序选择短。

请参阅itertools docs 了解我使用过的各种itertools 函数;生成子集的算法源自Recipes 部分中的powerset 示例。

from itertools import chain, combinations

def count_deletions(s):
    for t in chain.from_iterable(combinations(s, r) for r in range(len(s), 0, -1)):
        t = list(t)
        if t == sorted(t):
            return ''.join(t), len(s) - len(t)

# Some test data. 
data = [
    "abcdefg",
    "cba",
    "abcb",
    "vwzyx",
    "zvwzyx",
    "adabcef",
    "fantastic",
]

for s in data:
    print(s, count_deletions(s))

输出

abcdefg ('abcdefg', 0)
cba ('c', 2)
abcb ('abc', 1)
vwzyx ('vwz', 2)
zvwzyx ('vwz', 3)
adabcef ('aabcef', 1)
fantastic ('fntt', 5)

该数据集并不足以完全测试旨在解决此问题的算法,但我想这是一个不错的起点。 :)


更新

这是 Salvador Dali 在链接页面上提到的算法的 Python 3 实现。它比我之前的蛮力方法快得多,尤其是对于较长的字符串。

我们可以通过对字符串的副本进行排序,然后找到原始字符串和排序后的字符串的最长公共子序列(LCS)来找到最长的排序子序列。 Salvador 的版本从已排序的字符串中删除重复元素,因为他希望结果严格递增,但我们在这里不需要。

此代码仅返回所需的删除次数,但很容易修改它以返回实际排序的字符串。

为了使这个递归函数更高效,它使用了来自 functools 的 lru_cache 装饰器。

from functools import lru_cache

@lru_cache(maxsize=None)
def lcs_len(x, y):
    if not x or not y:
        return 0

    xhead, xtail = x[0], x[1:]
    yhead, ytail = y[0], y[1:]
    if xhead == yhead:
        return 1 + lcs_len(xtail, ytail)
    return max(lcs_len(x, ytail), lcs_len(xtail, y))

def count_deletions(s):
    lcs_len.cache_clear()
    return len(s) - lcs_len(s, ''.join(sorted(s)))

data = [
    "abcdefg",
    "cba",
    "abcb",
    "vwzyx",
    "zvwzyx",
    "adabcef",
    "fantastic",
]

for s in data:
    print(s, count_deletions(s))

输出

abcdefg 0
cba 2
abcb 1
vwzyx 2
zvwzyx 3
adabcef 1
fantastic 5

【讨论】:

  • 如果可能的话,你能帮忙看看java版本吗
  • @sagar 抱歉,我不懂 Java。但是,如果您能阅读 Python,将我的代码翻译成另一种语言应该不会太难。我建议您试一试,如果遇到困难,请在新问题中发布您的代码,可能会链接回这个问题,或者链接到本页顶部显示的重复目标。
  • 实际上 @lru_cache 似乎是 python 特定的库或实用程序,我在 java 中买不起,而且我只需要使用核心库。
  • @sagar 这里有一些关于在 Java 中做 memoization 的信息stackoverflow.com/questions/3623754/…
  • 我用 Java 中的 LCS 修复了这个问题。有一个算法可以相应地修改它
【解决方案2】:

希望它适用于所有情况:)

s = input()
s_2 = ''.join(sorted(set(s), key=s.index))
sorted_string = sorted(s_2)
str_to_list = list(s_2)
dif = 0

for i in range(len(sorted_string)):
    if sorted_string[i]!=str_to_list[i]:
        dif+=1

print(dif+abs(len(s)-len(s_2)))

【讨论】:

  • 不,这行不通。例如,在 'zvwzyx' 上它返回 5,但我们可以从该字符串生成 'vwz',因此删除计数只有 3。在 'adabcef' 上它应该返回 1,而不是 4。
  • 是的,你是对的
猜你喜欢
  • 2015-06-10
  • 1970-01-01
  • 2016-10-06
  • 1970-01-01
  • 2015-08-22
  • 1970-01-01
  • 2011-08-01
  • 2016-09-23
  • 2021-11-19
相关资源
最近更新 更多