题目列表
一、两个字符串的排列差
简单的模拟题,只要统计出字符串出现的下标,然后相减求和即可,代码如下
class Solution {
public:
int findPermutationDifference(string s, string t) {
int cnt[26]{};
int n = s.size();
for(int i=0;i<n;i++){
cnt[s[i]-'a']=i;
}
int ans = 0;
for(int i=0;i<n;i++){
int idx = t[i] - 'a';
ans += abs(cnt[idx]-i);
}
return ans;
}
};
二、从魔法师身上吸取的最大能量
这题同样是模拟题,题目要求每次固定向后跳跃k次,我们可以以不同的下标作为起点,往后面跳跃,计算出所有可能结果的最大值,时间复杂度为O(k*n),显然是过不了的。
如何优化时间复杂度?根据上面说的思路,我们会发现,当起点下标>=k时,我们就会开始重复的遍历之前已经遍历过的下标
那有没有方法让我们在一次遍历的过程中就能得到最优解,不用再去重复遍历呢?当然是有的,每到我们遍历到一个下标时,我们都有两者选择:1、继承从前面跳跃得到的能量继续往后跳,2、舍弃之前跳跃得到的能量,以当前下标为起始位置开始往后跳跃获得能量。我们只要取两者的最大值就行。(本质就是dp问题,和53. 最大子数组和类似,唯一的不同就在于我们是每隔k个选一个数)
代码如下
class Solution {
public:
int maximumEnergy(vector<int>& energy, int k) {
int n = energy.size();
int ans = INT_MIN;
for(int i = 0; i < k; i++){
int mx = 0;
for(int j=i;j<n;j+=k){
mx = max(mx,0) + energy[j];
}
ans = max(ans,mx);
}
return ans;
}
};
三、矩阵中的最大得分
这题要理解题意,其实也不是很难,只要举几个栗子,找到关键点即可。这里我们就拿第一个示例举例说明,我们按照它给的移动方法手动计算一下,7 - 5 + 14 - 7 = 14 - 5 = 9,就会发现我们计算的得分=未位置的值 - 初位置的值。
要求最大值,就是让末位置的值最大,初位置的值最小,同时要考虑到两个点的位置关系,如何做?我们只要枚举末位置,找以它未右下角的矩阵中的最小值作为初位置的值,求它们的最大值即可。
如何求二维数据中的最小值?和求解二维前缀和类似
代码如下
class Solution {
public:
int maxScore(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<int>>f(n+1,vector<int>(m+1,INT_MAX));
int ans = INT_MIN;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
f[i+1][j+1]=min(f[i+1][j],f[i][j+1]);
ans = max(ans,grid[i][j]-f[i+1][j+1]);// 注意起始位置和末位置不能相同,所以这里算得分时,没有将grid[i][j]加入矩阵最小值中
f[i+1][j+1]=min(f[i+1][j+1],grid[i][j]);
}
}
return ans;
}
};
四、找到分数最低的排列
这题就是暴力枚举所有可能的排列顺序,找到最小得分,然后倒过来找到达最小得分的排序,可以用状态压缩进行优化。思路不难想,主要是代码比较难写,可以看看下面的代码。
代码如下
class Solution {
public:
vector<int> findPermutation(vector<int>& nums) {
int n = nums.size();
int memo[1<<n][n];
memset(memo,-1,sizeof(memo));
// 两个参数,mask记录哪些数字被选过(用二进制0/1表示是否被选过---状态压缩),pre记录前一个被选的数字
function<int(int,int)>dfs=[&](int mask,int pre)->int{
if(mask==(1<<n)-1){
return abs(pre-nums[0]);
}
if(memo[mask][pre]!=-1)
return memo[mask][pre];
int res = INT_MAX;
for(int k=1;k<n;k++){
if((mask>>k) & 1) continue;
res = min(res,dfs(mask|1<<k,k)+abs(pre-nums[k]));
}
return memo[mask][pre]=res;
};
dfs(1,0);
vector<int> ans;
// 倒过来找排列顺序,由于路径是确定,所以该递归函数的时间复杂度为O(n)
function<void(int,int)>get_ans=[&](int mask,int pre){
ans.push_back(pre);
if(mask==(1<<n)-1)
return;
int final_res = dfs(mask,pre);
for(int k=1;k<n;k++){
if((mask>>k) & 1) continue;
if(dfs(mask|1<<k,k)+abs(pre-nums[k])==final_res){
get_ans(mask|1<<k,k);
break;
}
}
};
get_ans(1,0);
return ans;
}
};