Leetcode 3518. Smallest Palindromic Rearrangement II

  • Leetcode 3518. Smallest Palindromic Rearrangement II
    • 1. 解题思路
    • 2. 代码实现
  • 题目链接:Leetcode 3518. Smallest Palindromic Rearrangement II

1. 解题思路

这一题是题目Leetcode 3517. Smallest Palindromic Rearrangement I的升级版本,其主要的升级点就是要求按字典序排序第 k k k小的字符串,而不是简单的直接获取最小的字符串了。因此处理起来事实上要多做的部分也就是如何求这个第 k k k小的字符串。

我们首先看一下前置的内容,即首先我们需要找到这个回文的唯一部分,即其前半段所包含的全部字符,这个我们只需要将原始字符串进行一下字符统计,然后取所有字符的一半即可。此时,我们将其任意排列,然后拼凑其反向字符即可得到一个满足条件的回文。

因此,要求其第 k k k小的回文字符串,事实上也就是求这个前半段字符的第 k k k小的字符串排列。

显然,经过简单的数学知识,我们已知一个长度为 n n n的字符串序列,其可能的字符排列的种类一共有:

N = n ! Π i = 1 m n i ! N = \frac{n!}{\mathop{\Pi}\limits_{i=1}^{m}n_i!} N=i=1Πmni!n!

因此,我们只需要一个递推算法,逐一考察从头开始每一个元素依次从小到大摆放各个字符时其剩余的字符串排列的数目是否大于剩余需要的字符串排列数目即可。

用伪代码表示即为:

def get_kth_string(s, k):
    cnt = Counter(s)
    ans = ""
    while k > 1:
        for ch in [a..z]:
            cnt[ch] -= 1
            m = factorial(n)
            for _ch in [a..z]:
                m = m // factorial[_ch]
            if m >= k:
                ans += ch
            else:
                k -= m
                cnt[ch] += 1
        ans += "".join(ch * cnt[ch] for ch in [a..z])
    return ans

整体的算法复杂度就是 O ( N × 2 6 2 ) O(N \times 26^2) O(N×262)

2. 代码实现

给出最终的python代码实现如下:

class Solution:
    def get_kth_smallest_string(self, s, k):
        cnt = Counter(s)
        n = len(s)

        def c(n, m, upper):
            m = min(n-m, m)
            ans = 1
            for i in range(m):
                ans = ans * (n-i) // (i+1)
                if ans >= upper:
                    return ans
            return ans

        def get_count(cnt, upper):
            n = sum(cnt.values())
            ans = 1
            for m in cnt.values():
                ans = ans * c(n, m, upper)
                n -= m
                if ans >= upper:
                    return ans
            return ans

        tot = get_count(cnt, k)
        if k == 1:
            return "".join(sorted(s))
        elif tot < k:
            return ""

        def dfs(idx, k):
            if k == 1:
                return "".join([ch * cnt[ch] for ch in string.ascii_lowercase])
            for ch in string.ascii_lowercase:
                if cnt[ch] > 0:
                    cnt[ch] -= 1
                    tot = get_count(cnt, k)
                    if tot >= k:
                        return ch + dfs(idx+1, k)
                    k -= tot
                    cnt[ch] += 1
            return ""

        return dfs(0, k)

    def smallestPalindrome(self, s: str, k: int) -> str:
        cnt = Counter(s)
        mid = ""
        ans = ""
        for ch in string.ascii_lowercase:
            ans += ch * (cnt[ch] // 2)
            if cnt[ch] % 2 == 1:
                mid = ch
        if ans == "":
            return mid if k == 1 else ""
        
        ans = self.get_kth_smallest_string(ans, k)
        return ans + mid + ans[::-1] if ans != "" else ""

提交代码评测得到:耗时1055ms,占用内存19.4MB。

你可能感兴趣的:(leetcode笔记,leetcode,3518,leetcode,hard,leetcode周赛445,字典序排列,排列组合,回文)