forked from wisdompeak/LeetCode
-
Notifications
You must be signed in to change notification settings - Fork 1
[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
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
98 changes: 98 additions & 0 deletions
...3518.Smallest-Palindromic-Rearrangement-II/3518.Smallest-Palindromic-Rearrangement-II.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.