检查相同字母间的距离
解题思路
题目比较长,读起来有点费劲, 意思就是说每个在s中出现的字母, 都会出现两次, 如果这两次之间的字母个数和distance数组中记录的都一致的话,就返回真,否则为假.
首先要把每个字母ch之间出现的其他字母个数记录在一个map 中, 为了方便映射到distance数组, key 就为ch-‘a’
为了节省内存, 这个map的功能可以服用, 当字母第一次出现时,用map 记录下它的下标,当字母第二次出现时,就可以利用第一次出现的 下标计算出两次出现之间有多少其他字母
最后与distance数组比较
代码实现
class Solution {
public boolean checkDistances(String s, int[] distance) {
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0;i<s.length();i++){
int num = s.charAt(i) - 'a';
if(!map.containsKey(num)){
map.put(num,i);
}else{
map.put(num,i - map.get(num)-1);
}
}
for(Map.Entry<Integer,Integer> entry:map.entrySet()){
if(distance[entry.getKey()] != entry.getValue()){
return false;
}
}
return true;
}
}
恰好移动 k 步到达某一位置的方法数目
解题思路
假设规定从左到右走为正方向,从右往左走为负方向, 假设从开始位置走到结束位置有a步走了正方向, 那么在负方向就走了 k-a步, 开始位置和结束之间的步数为 d = |endPos - endPos|, 那么 a - (k-a) = d, 那么a = (k+d)/2, 那么题目的意思就转变为k步中选(k+d)/2步走正方向的选择数.使用递推的方式求组合数
代码
class Solution {
private static int MOD = 1000000007;
public int numberOfWays(int startPos, int endPos, int k) {
int d = Math.abs(endPos - startPos);
if(k<d || (k+d)%2 == 1){
return 0;
}
// i 步当中 j 步走正方向的走法
int[][] dp = new int[k+1][k+1];
for(int i = 0;i<=k;i++){
dp[i][0] = 1;
for(int j = 1;j<=i;j++){
dp[i][j] = (dp[i-1][j] + dp[i-1][j-1]) % MOD;
}
}
return dp[k][(d+k)/2];
}
}
可能这道题还是过于抽象,我们来看另一道相似的
不同的路径
解题思路
一种比较简单直白的思路
我们使用dp[i][j] 记录机器人走到[i,j]这个位置的路径数,我们知道它要走到[i,j], 就要么从[i,j-1]的位置往右一步,要么从[i-1,j]的位置往下走一步, 所以,它走到[i,j]的路径数等于[i-1,j]的路径数加上[i-1,j]的路径数
另外,第一行 和 第一列 的路径数都只能是1, 因此有如下代码
class Solution {
public int uniquePaths(int m, int n) {
int[][]dp = new int[m][n];
Arrays.fill(dp[0],1);
for(int i = 0;i<m;i++){
dp[i][0] = 1;
}
for(int i = 1;i<m;i++){
for(int j = 1;j<n;j++){
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
}
这道还可以从另外一种角度来思考, 机器人走[0,0] 走到[m,n] 一共需要走m+n-2步, 它一共往下走了m-1步, 那么这道题就可以变为m+n-2步中选择m-1步往下走的选择数,因此也可以这样写.
这种做法就和 恰好移动 k 步到达某一位置的方法数目 的解题思路一致
class Solution {
public int uniquePaths(int m, int n) {
int[][]dp = new int[m+n-1][m];
for(int i = 0;i<m+n-1;i++){
dp[i][0] = 1;
for(int j = 1;j<=Math.min(i,m-1);j++){
dp[i][j] = dp[i-1][j] + dp[i-1][j-1];
}
}
return dp[m+n-2][m-1];
}
}
解题思路
利用dp[i]记录以nums[i]为结尾的最长优雅子数组的长度, 因为子数组连续的, dp[i]的最大长度只能是dp[i-1]+1,此时nums[i] 前dp[i-1]个元素逻辑与的结果都是0, 只能它的长度是多少,看它能连续跟多少个之前的元素逻辑与的结果为0
代码实现
class Solution {
public int longestNiceSubarray(int[] nums) {
int max = 1;
int[] dp = new int[nums.length];
Arrays.fill(dp,1);
for(int i = 1;i<nums.length;i++){
int cnt = 1;
// 往前遍历,只需要遍历到前dp[i-1]个元素即可,
for(int j = 1;j<=dp[i-1];j++){
if((nums[i]&nums[i-j]) == 0){
cnt++;
}else{
break;
}
}
dp[i] = cnt;
max = Math.max(max,cnt);
}
return max;
}
}
从上面的过程可以看到dp数组其实每次都只是利用了前一个元素而已,因此可以只使用一个变量来表示,从而将空间复杂度从O(n)优化到O(1)
class Solution {
public int longestNiceSubarray(int[] nums) {
int max = 1;
int pre = 1;
for(int i = 1;i<nums.length;i++){
int cnt = 1;
for(int j = 1;j<=pre;j++){
if((nums[i]&nums[i-j]) == 0){
cnt++;
}else{
break;
}
}
pre = cnt;
max = Math.max(max,cnt);
}
return max;
}
}
会议室III
解题思路
代码
class Solution {
public int mostBooked(int n, int[][] meetings) {
// [0]为会议结束时间, [1]为会议室编号
PriorityQueue<int[]> queue = new PriorityQueue<>((a,b)->a[0]==b[0]?a[1]-b[1]:a[0]-b[0]);
// 初始化会议的结束时间为0
for(int i = 0;i<n;i++){
queue.offer(new int[]{0,i});
}
// 记录每个会议室开会议的次数
int[]room = new int[n];
Arrays.sort(meetings,(a,b)->a[0]-b[0]);
for(int[]m:meetings){
//将已经结束的会议室初始化为可用
Set<Integer> set = new HashSet<>();
while(!queue.isEmpty() && queue.peek()[0]<=m[0]){
set.add(queue.poll()[1]);
}
for(int r:set){
queue.offer(new int[]{0,r});
}
int[] q = queue.poll();
room[q[1]]++;
int d = m[1]-m[0];
queue.offer(new int[]{Math.max(q[0],m[0])+d,q[1]});
}
int max = 0;
int ans = 0;
for(int i = 0;i<n;i++){
if(room[i]>max){
max = room[i];
ans = i;
}
}
return ans;
}
}