3518.最小回文排列 II:试填法(组合数学)
【LetMeFly】3518.最小回文排列 II:试填法(组合数学)
力扣题目链接:https://leetcode.cn/problems/smallest-palindromic-rearrangement-ii/
给你一个 回文 字符串 s 和一个整数 k。
返回 s 的按字典序排列的 第 k 小 回文排列。如果不存在 k 个不同的回文排列,则返回空字符串。
注意: 产生相同回文字符串的不同重排视为相同,仅计为一次。
如果一个字符串从前往后和从后往前读都相同,那么这个字符串是一个 回文 字符串。
排列 是字符串中所有字符的重排。
如果字符串 a 按字典序小于字符串 b,则表示在第一个不同的位置,a 中的字符比 b 中的对应字符在字母表中更靠前。
如果在前 min(a.length, b.length) 个字符中没有区别,则较短的字符串按字典序更小。
示例 1:
输入: s = "abba", k = 2
输出: "baab"
解释:
"abba"的两个不同的回文排列是"abba"和"baab"。- 按字典序,
"abba"位于"baab"之前。由于k = 2,输出为"baab"。
示例 2:
输入: s = "aa", k = 2
输出: ""
解释:
- 仅有一个回文排列:
"aa"。 - 由于
k = 2超过了可能的排列数,输出为空字符串。
示例 3:
输入: s = "bacab", k = 1
输出: "abcba"
解释:
"bacab"的两个不同的回文排列是"abcba"和"bacab"。- 按字典序,
"abcba"位于"bacab"之前。由于k = 1,输出为"abcba"。
提示:
1 <= s.length <= 104s由小写英文字母组成。- 保证
s是回文字符串。 1 <= k <= 106
解题方法:试填法
解题思路
我们只需要考虑前半个字符串,确定了前半个字符串就能翻转得到后半个字符串。如果字符串长度为奇数,则中间位置的元素只出现了奇数次,最终字符串中间的元素一定还是它,所以无需额外考虑。
遍历前半个字符串,统计出每种字符的出现次数($cnt[i]$代表第$i$个小写字母的出现次数)。计算共能构造多少种字符串,如果不能构造$k$种,则直接返回空字符串。
怎么通过每种字母的出现次数$cnt$计算共能构造出多少种字符串呢?组合数学要出场了:
假设前半个字符串共有$len$个元素,我们可以先在里面选择$cnt[0]$个位置放
a($C_{len}^{cnt[0]}$),再在剩下的$len-cnt[0]$个位置选$cnt[1]$个放b($C_{len-cnt[0]}^{cnt[1]}$),再在剩下的$len-cnt[0]-cnt[1]$个位置选$cnt[2]$个放c($C_{len-cnt[0]-cnt[1]}^{cnt[2]}$),…,放完所有26种字母为止。
假设总方案数$\geq k$,怎么确定第一个字母是谁呢?从a到z一个一个地试呗。
(假设字符串中存在字母
a)我们先把第一个字母设置为a,计算a开头时候字符串一共有多少种。由于第一位选择了
a,可变的字符串是除了已选字符串的剩下部分。令$cnt[0]-1$,令$len-1$,使用和计算总方案数时候一样的方法即可求出 第一位选a时候的字符串方案数。如果第一位选
a时总方案数小于$k$,说明第$k$小字符串的开头一定不是a。令$k$减去a开头时候的方案数,并开始尝试第一个字母是b。直到尝试到某个字母开头时的方案数大于等于当时的$k$时,这个字母就是第一个要选的字母。
接下来第二个字母、第三个字母同理,直至我们选完了前半个字符串的所有字母,答案所需的第$k$小回文串就得到了。
具体细节
1. 最多k
由于字符串长度是$10^4$级别,所以$C_{len}^{cnt[0]}$可能非常大。
不过好在$k$最大值是$10^6$,所以我们可以在计算过程中一旦发现结果大于$k$就停止计算。
2. C的计算方式
$$C_a^b=\frac{a\times(a-1)\times\cdots\times(a-b+1)}{b\times(b-1)\times\cdots\times 1}\=\frac{a\times(a-1)\times\cdots\times(a-b+1)}{1\times 2\times\cdots b}\=\frac{a}{1}\times\frac{a-1}2\times\cdots\times\frac{a-b+1}{b}$$
我们可以一个乘法一个乘法地算,一旦$\geq k$就停止。
这么算会出现分数吗?
不会。
- 第一个分母是$1$,分子一定是$1$的倍数;
- 第二个分母是$2$,前两个分子中一定有$2$的倍数;
- 第三个分母是$3$,前三个分子中一定有$3$的倍数;
- $\cdots$
一旦$\geq k$就停止,会停止过早吗?最终结果会又变得$\lt k$ 了吗?
不会。我们可以使用一个技巧,由于在$a$个位置中选$b$个等价于在$a$个位置中选$a-b$个,所以$C_a^b=C_a^{a-b}$。
我们可以令$b$为$b$和$a-b$中较小的那个。也就是说$b\leq \frac{a}2$,即使分子在递减分母在递增,到最后的$\frac{a-b+1}{b}$也一定$\gt 1$。
时空复杂度分析
- 时间复杂度$O(nC(C+\log k))$,其中$n=len(s)$,$C=26$。计算总共有多少种方案数时(methods函数),$all$最多累乘$O(\log k)$个大于$1$的数。
- 空间复杂度$O(n+C)$。
AC代码
C++
1 | |
同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
千篇源码题解已开源,无特殊声明部分均非AI。