Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[pull] master from wisdompeak:master #317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
pull merged 5 commits into AlgorithmAndLeetCode:master from wisdompeak:master
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using LL =long long;
class Solution {
public:
int comb[5001][2501];

int getComb(int m, int n)
{
if (m<n) return 0;
if (n<=(m+1)/2)
return comb[m][n];
else
return comb[m][m-n];
}

void precompute(int n)
{
for (int i = 0; i <= n; ++i)
{
comb[i][0] = 1;
if (i==0) continue;
for (int j = 1; j <= (i+1)/2; ++j)
{
comb[i][j] = getComb(i-1, j-1) + getComb(i-1,j);
comb[i][j] = min(comb[i][j], INT_MAX/2);
}
}
}

LL countPermutations(const vector<int>& nums) {

int total = accumulate(nums.begin(), nums.end(), 0);

LL ret = 1;
for (int i=0; i<26; i++)
{
if (nums[i]!=0)
{
ret *= getComb(total, nums[i]);
if (ret >= INT_MAX/2) return INT_MAX/2;
total -= nums[i];
}
}
return ret;
}

string smallestPalindrome(string s, int k)
{
int n = s.size();
vector<int>nums(26);
for (int i=0; i<n/2; i++)
nums[s[i]-'a']+=1;

precompute(n/2);

string ret;
LL curCount = 0;
dfs(k, nums, ret, curCount);

if (curCount != k || ret.size() != n/2) return "";

string tt=ret;
reverse(tt.begin(), tt.end());
if (n%2==1)
ret.push_back(s[n/2]);
ret += tt;

return ret;

}

void dfs(LL k, vector<int>&nums, string& ret, LL& curCount)
{
int total = accumulate(nums.begin(), nums.end(), 0);
if (total == 0) {
curCount+=1;
return;
}

for (int i=0; i<26; i++)
{
if (nums[i]==0) continue;
nums[i]-=1;
LL temp = countPermutations(nums);

if (curCount + temp < k)
{
curCount += temp;
nums[i]++;
}
else
{
ret.push_back('a'+i);
dfs(k, nums, ret, curCount);
break;
}
}
}
};
35 changes: 35 additions & 0 deletions Math/3518.Smallest-Palindromic-Rearrangement-II/Readme.md
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
### 3518.Smallest-Palindromic-Rearrangement-II

很明显本题的核心就是,我们取原字符串前一半的字母,问这些字母所能组成的从小到大的第k个字典序的排列是什么。因为有重复的字符,这里要求重复的排列只算一种。

关于n个字母的排列,求第k大的字典序,是一个比较常规的题目了。我们只需要从按头到尾的顺序、把候选字母从小到大地进行尝试即可。比如说,我们尝试第一个位置填写a,并且计算出后面n-1个位置如果有m种排列,那么就可以根据m和k的大小关系进行决策:如果m<k,那么说明第一个位置填写a是不够的,那么我们就可以尝试填写b(或者其他更大的候选字符);如果m>=k,那么我们就能确定第一个位置必然是a,此时进行递归处理第二个位置即可。

由此,我们需要一个函数`countPermutations(vector<int>&nums)`,表示给定一系列字母及其个数的情况下,有多少种不同的排列。其中nums是一个长度为26的数组,记录每种字符的个数。假设总共的字符个数是n。此时最容易想到的算法就是 `count = n!/(t0!)*(t1!)...*(t25!)`,其中ti表示每种字符的个数。因为n达到了1e4,其阶乘是天文数字,必然会溢出;并且这涉及到了大数的除法,我们无法保证精度。此时就应该想到第二种算法,就是 `count = C(n,t0)*C(n-t0,t1)*C(n-t0-t1,t2)*...`这里就规避了除法的精度问题,但是溢出问题依然得不到解决。

此时注意到k不超过1e6。一旦count的计算需要涉及到超大的阶乘数,那么必然会远远超过k。当这种情况发生时,我们其实并不需要知道count的精确数值,因为根据之前的说法,我们对字母选择的决策其实是很明显的了。因此我们在计算count甚至在计算组合数本身时,如果能预期到它很大,我们直接就返回INT_MAX/2之类的超大数来影响决策即可,而不需要计算它实际的数值。

此外,本题对于空间的利用需要达到极致。我们知道,可以用n^2的时间和空间来提前处理Comb(n,x)级别的组合数。通常我们只能开到comb[1000][1000]的规模。但是利用到`C(m,n)=C(m,m-n)`的性质,我们可以最大开辟数组空间达到comb[5001][2501]。代码如下:
```cpp
int comb[5001][2501];
int getComb(int m, int n)
{
if (m<n) return 0;
if (n<=(m+1)/2)
return comb[m][n];
else
return comb[m][m-n];
}
void precompute(int n)
{
for (int i = 0; i <= n; ++i)
{
comb[i][0] = 1;
if (i==0) continue;
for (int j = 1; j <= (i+1)/2; ++j)
{
comb[i][j] = getComb(i-1, j-1) + getComb(i-1,j);
comb[i][j] = min(comb[i][j], INT_MAX/2);
}
}
}
```
1 change: 1 addition & 0 deletions Readme.md
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,7 @@
[3405.Count-the-Number-of-Arrays-with-K-Matching-Adjacent-Elements](https://github.com/wisdompeak/LeetCode/tree/master/Math/3405.Count-the-Number-of-Arrays-with-K-Matching-Adjacent-Elements) (H-)
[3428.Maximum-and-Minimum-Sums-of-at-Most-Size-K-Subsequences](https://github.com/wisdompeak/LeetCode/tree/master/Math/3428.Maximum-and-Minimum-Sums-of-at-Most-Size-K-Subsequences) (M+)
[3463.Check-If-Digits-Are-Equal-in-String-After-Operations-II](https://github.com/wisdompeak/LeetCode/tree/master/Math/3463.Check-If-Digits-Are-Equal-in-String-After-Operations-II) (H+)
[3518.Smallest-Palindromic-Rearrangement-II](https://github.com/wisdompeak/LeetCode/tree/master/Math/3518.Smallest-Palindromic-Rearrangement-II) (H)
* ``Numerical Theory``
[204.Count-Primes](https://github.com/wisdompeak/LeetCode/tree/master/Math/204.Count-Primes) (M)
[343.Integer-Break](https://github.com/wisdompeak/LeetCode/tree/master/Math/343.Integer-Break) (H-)
Expand Down
25 changes: 25 additions & 0 deletions Template/Math/Combination-Number.cpp
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,31 @@ for (int i = 0; i <= n; ++i)
}
}

/*********************************/
// Version 1.5: compute all C(n,m) saved in comb with maximum space capability
int comb[5001][2501];
int getComb(int m, int n)
{
if (m<n) return 0;
if (n<=(m+1)/2)
return comb[m][n];
else
return comb[m][m-n];
}
void precompute(int n)
{
for (int i = 0; i <= n; ++i)
{
comb[i][0] = 1;
if (i==0) continue;
for (int j = 1; j <= (i+1)/2; ++j)
{
comb[i][j] = getComb(i-1, j-1) + getComb(i-1,j);
comb[i][j] = min(comb[i][j], INT_MAX/2);
}
}
}

/*********************************/
// Version 2: Compute C(n,m) on demand based on definition
long long help(int n, int m)
Expand Down

AltStyle によって変換されたページ (->オリジナル) /