📜  通过从长度为2K的回文子字符串中删除K长度前缀,可以将字符串的词典排列最小排列为长度K(1)

📅  最后修改于: 2023-12-03 15:28:24.539000             🧑  作者: Mango

通过回文子字符串变换字符串的词典最小排列

本文介绍如何通过从长度为2K的回文子字符串中删除K长度前缀,将字符串的词典排列转换为最小排列。

1. 什么是词典最小排列?

字符串的词典排列是指将若干个字符串按照字典序排序后得到的全排列中的第一个排列。例如,字符串集合 { "apple", "banana", "orange" } 的词典最小排列即为 { "apple", "banana", "orange" },因为在该集合中,"apple" 的字典序最小。

2. 如何将字符串转换为回文子字符串?

给定一个长度为 N 的字符串,首先在该字符串的左侧添加 N 个不同的字符,得到长度为 2N 的字符串 S,例如:

S = "0123...N-1" + "original string"

然后对该字符串进行 Manacher 算法,得到回文半径数组 P。根据回文半径数组生成回文子字符串的过程如下:

  • 对于每个位置 i,令 K = P[i],将从位置 i - K 到位置 i + K 的子串提取出来;
  • 如果该子串的长度为偶数,令它的左半部分的长度为 L = K - 1,右半部分的长度为 R = K;
  • 如果该子串的长度为奇数,令它的左半部分的长度和右半部分的长度都为 K - 1。

经过上述过程,可以得到长度为 2N 的回文子字符串集合,其中第 i 个回文子字符串的中心为原字符串中的第 i - N 个位置。

3. 如何转换为词典最小排列?

假设原字符串的长度为 K,对于长度为 2K 的回文子字符串,我们需要删除左侧 K 个字符,然后将剩余的 K 个字符按照字典序排序。具体做法如下:

  • 对于每个位置 i,令 K = P[i];
  • 删除从位置 i - K 到位置 i - K + K 的 K 个字符;
  • 将从位置 i - K + K 到位置 i 的 K 个字符按照字典序排序,然后插入到原字符串的第 i - N 个位置。

由于该操作只改变了每个回文子字符串的左侧 K 个字符,因此得到的字符串的词典最小排列即为原字符串的词典最小排列。

4. 代码实现

下面是使用 Python 实现上述过程的代码片段:

def to_palindrome_substrings(s: str) -> List[str]:
    n = len(s)
    t = "#".join("^{}$".format(s))
    p = [0] * len(t)
    c = r = 0
    for i in range(1, len(t) - 1):
        p[i] = (r > i) and min(r - i, p[2*c - i])
        while t[i + p[i] + 1] == t[i - p[i] - 1]:
            p[i] += 1
        if i + p[i] > r:
            c, r = i, i + p[i]
    res = []
    for i in range(2*n + 1):
        k = p[i]
        if k == 0:
            continue
        k -= 1
        if i < k or i + k >= 2*n + 1:
            continue
        is_odd = (k + 1) % 2 == 1
        left = (k + 1) // 2
        right = k // 2
        if is_odd:
            res.append(s[i//2-left:i//2+right+1])
        else:
            res.append(s[i//2-left+1:i//2+right+1])
    return res

def to_lexicographically_smallest_permutation(s: str, k: int) -> str:
    subs = to_palindrome_substrings(s)
    subs = [sub[k:] for sub in subs if len(sub) >= k + 1]
    subs.sort()
    i, j = 0, 0
    res = []
    while i < len(s) or j < len(subs):
        if j == len(subs) or (i < len(s) and s[i] < subs[j][0]):
            res.append(s[i])
            i += 1
        else:
            res += list(subs[j])
            j += 1
    return "".join(res)

代码中 to_palindrome_substrings() 函数用于将字符串转换为回文子字符串集合,to_lexicographically_smallest_permutation() 函数用于将字符串转换为词典最小排列。函数的输入和输出格式在代码中均已标注。