📅  最后修改于: 2023-12-03 15:28:24.539000             🧑  作者: Mango
本文介绍如何通过从长度为2K的回文子字符串中删除K长度前缀,将字符串的词典排列转换为最小排列。
字符串的词典排列是指将若干个字符串按照字典序排序后得到的全排列中的第一个排列。例如,字符串集合 { "apple", "banana", "orange" } 的词典最小排列即为 { "apple", "banana", "orange" },因为在该集合中,"apple" 的字典序最小。
给定一个长度为 N 的字符串,首先在该字符串的左侧添加 N 个不同的字符,得到长度为 2N 的字符串 S,例如:
S = "0123...N-1" + "original string"
然后对该字符串进行 Manacher 算法,得到回文半径数组 P。根据回文半径数组生成回文子字符串的过程如下:
经过上述过程,可以得到长度为 2N 的回文子字符串集合,其中第 i 个回文子字符串的中心为原字符串中的第 i - N 个位置。
假设原字符串的长度为 K,对于长度为 2K 的回文子字符串,我们需要删除左侧 K 个字符,然后将剩余的 K 个字符按照字典序排序。具体做法如下:
由于该操作只改变了每个回文子字符串的左侧 K 个字符,因此得到的字符串的词典最小排列即为原字符串的词典最小排列。
下面是使用 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()
函数用于将字符串转换为词典最小排列。函数的输入和输出格式在代码中均已标注。