3516. 找到最近的人
题目链接: 3516. 找到最近的人

题目解析:
找到最近的人,z是不动的,我们需要知道,x 到 z 的距离和 y 到 z 的距离,又因为 x 和 y 的移动速度相同,所以,谁离 z 越近,越先到达 z 位置,
x 到 z 的距离:abs(x-z)
y 到 z 的距离:abs(y-z)
x 先到达,返回 1,y 先到达,返回 2,同时到达,返回 0
解法一:计算距离
class Solution {
public:
int findClosest(int x, int y, int z) {
int a = abs(x - z), b = abs(y - z);
return a < b ? 1 : (a == b ? 0 : 2);
}
};
3517. 最小回文排列 I
题目链接: 3517. 最小回文排列 I

题目解析:
一个回文 字符串 s,返回 s 的按字典序排列的最小回文序列,要求 s 首先是回文序列,再排序,这样,我们只需排列 s 的前半部分就可以了。
- 注意,这里的mid表示的是下标,不是长度
找到 s 的中点mid,如果 s 是奇数序列,mid是中点,如果是偶数序列,mid是偏右的中点
排列前半部分字符串,也就是[0,mid),这样前半部分字符串就有序了,如果是奇数,则加上中间的字符s[mid],如果是偶数,则不用加
把前半部分字符串反转一下就是后半部分字符串,然后把两个字符串拼接一下,就得到了 最小 回文排列。
解法一:排序前半部分+反转
class Solution {
public:
string smallestPalindrome(string s) {
int n = s.size();
if (n == 1)
return s;
int mid = n / 2;
string tmp = s.substr(0, mid);
sort(tmp.begin(), tmp.end());
string ret = tmp;
if (n & 1)
ret += s[mid];
reverse(tmp.begin(), tmp.end());
ret += tmp;
return ret;
}
};
3518. 最小回文排列 II
题目链接: 3518. 最小回文排列 II

题目解析:

解法一:试填法+组合数学
class Solution {
public:
string smallestPalindrome(string s, int k) {
int n=s.size();
int m=n/2;
//统计前半部分字符出现的频率
vector<int> cnt(26,0);
for(int i=0;i<m;i++)
{
cnt[s[i]-'a']++;
}
//C(n,m)
auto comb = [&](int n, int m) -> int
{
m=min(m,n-m);
long long res=1;
for(int i=1;i<=m;i++)
{
res=res*(n-i+1)/i;
if(res>=k) return k;//满足条件了,可以提前退出,减少计算
}
return res;
};
//统计sz个位置中有多少种排列
auto perm = [&](int sz) -> int
{
long long res=1;
//从cnt中挑选字符
for(int i=0;i<26;i++)
{
if(cnt[i]==0) continue;
res*=comb(sz,cnt[i]);
if(res>=k) return k;//满足条件了,可以提前退出,减少计算
//更新字符串中的空位置
sz-=cnt[i];
}
return res;
};
//k太大了
if(perm(m)<k) return "";
//构建左半边字符串
string tmp(m,0);
for(int i=0;i<m;i++)
{
for(int j=0;j<26;j++)
{
if(cnt[j]==0) continue;
//把当前字符填入字符串中
cnt[j]--;
//计算剩下的排列
//m-i-1表示,从下标m位置,到下标i位置,之间的长度,都是左开右开(i,m);
int p=perm(m-i-1);
//剩下的排列大于等于K,说明该字符可以填
if(p>=k)
{
tmp[i]='a'+j;//细节,填充的是字符,不是该字符出现的频率
break;
}
//说明该位置不可以填,填更大的字符
k-=p;
cnt[j]++;
}
}
//构建整体字符串
string ret=tmp;
if(n&1) ret+=s[n/2];
reverse(tmp.begin(),tmp.end());
ret+=tmp;
return ret;
}
};