LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

771. 宝石与石头

给定字符串J 代表石头中宝石的类型,和字符串 S代表你拥有的石头。 S 中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。

J 中的字母不重复,J 和 S中的所有字符都是字母。字母区分大小写,因此"a"和"A"是不同类型的石头。

示例 1:

输入: J = "aA", S = "aAAbbbb" 输出: 3 示例 2:

输入: J = "z", S = "ZZ" 输出: 0 注意:

S 和 J 最多含有50个字母。 J 中的字符不重复。

class Solution {
     public int numJewelsInStones(String J, String S) {
        if (S == null || S.isEmpty()) return 0;
        if (J == null || J.isEmpty()) return 0;

        byte[] bytes = new byte[58];
        int count = 0;
        for (char ch : J.toCharArray()) {
            bytes[ch - 65] = 1;
        }
        for (char ch : S.toCharArray()) {
            if(bytes[ch -65] == 1) {
                count++;
            };
        }
        return count;
    }
}

773. 滑动谜题

在一个 2 x 3 的板上(board)有 5 块砖瓦,用数字 1~5 来表示, 以及一块空缺用 0 来表示.

一次移动定义为选择 0 与一个相邻的数字(上下左右)进行交换.

最终当板 board 的结果是 [[1,2,3],[4,5,0]] 谜板被解开。

给出一个谜板的初始状态,返回最少可以通过多少次移动解开谜板,如果不能解开谜板,则返回 -1 。

示例:

输入:board = [[1,2,3],[4,0,5]] 输出:1 解释:交换 0 和 5 ,1 步完成 输入:board = [[1,2,3],[5,4,0]] 输出:-1 解释:没有办法完成谜板 输入:board = [[4,1,2],[5,0,3]] 输出:5 解释: 最少完成谜板的最少移动次数是 5 , 一种移动路径: 尚未移动: [[4,1,2],[5,0,3]] 移动 1 次: [[4,1,2],[0,5,3]] 移动 2 次: [[0,1,2],[4,5,3]] 移动 3 次: [[1,0,2],[4,5,3]] 移动 4 次: [[1,2,0],[4,5,3]] 移动 5 次: [[1,2,3],[4,5,0]] 输入:board = [[3,2,4],[1,5,0]] 输出:14 提示:

board 是一个如上所述的 2 x 3 的数组. board[i][j] 是一个 [0, 1, 2, 3, 4, 5] 的排列.

PS: BFS搜索,交换,然后每一次都看是不是匹配

class Solution {
    private static int ROW = 2;
    private static int COL = 3;
    private static final String RESULT = "123450";
    Set<String> set;
    LinkedList<String> queue;
    
    public int slidingPuzzle(int[][] board) {
    	if ((board.length != ROW) || (board[0].length != COL)) {
    		return 0;
    	}
    	set = new HashSet<>();
    	int time = 0;
    	char[] boardStr = getCharFromBoard(board);
    	if (RESULT.equals(Arrays.toString(boardStr))) {
    		return 0;
    	}
    	queue = new LinkedList<>();
    	queue.addLast(new String(boardStr));
    	while (!queue.isEmpty()) {
    		int size = queue.size();
    		for (int i = 0; i < size; i++) {
    			String node = queue.pollFirst();
    			if (RESULT.equals(node)) {
    				return time;
    			}
    			boardStr = node.toCharArray();
    			if (boardStr[0] == '0') {
    				move(boardStr, 0, 1);
    				move(boardStr, 0, 3);
    			} else if (boardStr[1] == '0') {
    				move(boardStr, 1, 0);
    				move(boardStr, 1, 2);
    				move(boardStr, 1, 4);
    			} else if (boardStr[2] == '0') {
    				move(boardStr, 2, 1);
    				move(boardStr, 2, 5);
    			} else if (boardStr[3] == '0') {
    				move(boardStr, 3, 0);
    				move(boardStr, 3, 4);
    			} else if (boardStr[4] == '0') {
    				move(boardStr, 4, 1);
    				move(boardStr, 4, 3);
    				move(boardStr, 4, 5);
    			} else if (boardStr[5] == '0') {
    				move(boardStr, 5, 2);
    				move(boardStr, 5, 4);
    			}
    		}
    		time++;
    	}
    	return -1;
    }
    
    private void move(char[] string, int i, int j) {
		swap(string, i, j);
		String str = new String(string);
		if (!set.contains(str)) {
			queue.addLast(str);
			set.add(str);
		}
		swap(string, i, j);
    }
    
    private void swap (char[] string, int i, int j) {
    	char temp = string[i];
    	string[i] = string[j];
    	string[j] = temp;
    }
    
    private char[] getCharFromBoard(int[][] board) {
    	char[] res = new char[6];
    	res[0] = (char) ('0' + board[0][0]);
    	res[1] = (char) ('0' + board[0][1]);
    	res[2] = (char) ('0' + board[0][2]);
    	res[3] = (char) ('0' + board[1][0]);
    	res[4] = (char) ('0' + board[1][1]);
    	res[5] = (char) ('0' + board[1][2]);
    	return res;
    }
}

775. 全局倒置与局部倒置

数组 A 是 [0, 1, ..., N - 1] 的一种排列,N 是数组 A 的长度。全局倒置指的是 i,j 满足 0 <= i < j < N 并且 A[i] > A[j] ,局部倒置指的是 i 满足 0 <= i < N 并且 A[i] > A[i+1] 。

当数组 A 中全局倒置的数量等于局部倒置的数量时,返回 true 。

示例 1:

输入: A = [1,0,2] 输出: true 解释: 有 1 个全局倒置,和 1 个局部倒置。 示例 2:

输入: A = [1,2,0] 输出: false 解释: 有 2 个全局倒置,和 1 个局部倒置。 注意:

A 是 [0, 1, ..., A.length - 1] 的一种排列 A 的长度在 [1, 5000]之间 这个问题的时间限制已经减少了。

PS: 分析题 数组A中的元素是0到n-1 这个题的两种倒置,一种是差1的倒置,一种是差好几位的倒置 我们可以这么看,差1的倒置,只能一个,差好几位的倒置如果差的位置大于1 那么差好几位的倒置一定会比差1的倒置多的 所以这个题,就变成了,当前的位置如果比他的值差了2,那么就不相等了

class Solution {
      public boolean isIdealPermutation(int[] A) {
        int N = A.length;
        int floor = N;
        for (int i=N-1; i>=2; --i) {
            floor = Math.min(floor, A[i]);
            if (A[i-2] > floor) return false;
        }
        return true;
    }
}

777. 在LR字符串中交换相邻字符

在一个由 'L' , 'R' 和 'X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作。一次移动操作指用一个"LX"替换一个"XL",或者用一个"XR"替换一个"RX"。现给定起始字符串start和结束字符串end,请编写代码,当且仅当存在一系列移动操作使得start可以转换成end时, 返回True。

示例 :

输入: start = "RXXLRXRXL", end = "XRLXXRRLX"
输出: True
解释:
我们可以通过以下几步将start转换成end:
RXXLRXRXL ->
XRXLRXRXL ->
XRLXRXRXL ->
XRLXXRRXL ->
XRLXXRRLX

提示:

1 <= len(start) = len(end) <= 10000。 start和end中的字符串仅限于'L', 'R'和'X'。

PS: 根据题目得出,L只能想左走,R只能向右走 双指针查询,一个在start里找,一个在end里找 start的找到L的时候,start的索引一定要<end的索引,因为L只能像左走, 如果相反了的话,那么我的L会比end的多,无法匹配 反之,end的R也是一样的

class Solution {
    public boolean canTransform(String start, String end) {
        for (int i = 0, j = 0, len = start.length(); ; i++, j++) {
            while (i < len && start.charAt(i) == 'X') i++;
            while (j < len && end.charAt(j) == 'X') j++;
            if (i == len && j == len) return true;
            if (i == len || j == len || start.charAt(i) != end.charAt(j)) return false;
            if (start.charAt(i) == 'L' && i < j) return false;
            if (end.charAt(j) == 'R' && i > j) return false;
        }
    }
}

778. 水位上升的泳池中游泳

在一个 N x N 的坐标方格 grid 中,每一个方格的值 grid[i][j] 表示在位置 (i,j) 的平台高度。

现在开始下雨了。当时间为 t 时,此时雨水导致水池中任意位置的水位为 t 。你可以从一个平台游向四周相邻的任意一个平台,但是前提是此时水位必须同时淹没这两个平台。假定你可以瞬间移动无限距离,也就是默认在方格内部游动是不耗时的。当然,在你游泳的时候你必须待在坐标方格里面。

你从坐标方格的左上平台 (0,0) 出发。最少耗时多久你才能到达坐标方格的右下平台 (N-1, N-1)?

示例 1:

输入: [[0,2],[1,3]] 输出: 3 解释: 时间为0时,你位于坐标方格的位置为 (0, 0)。 此时你不能游向任意方向,因为四个相邻方向平台的高度都大于当前时间为 0 时的水位。

等时间到达 3 时,你才可以游向平台 (1, 1). 因为此时的水位是 3,坐标方格中的平台没有比水位 3 更高的,所以你可以游向坐标方格中的任意位置 示例2:

输入: [[0,1,2,3,4],[24,23,22,21,5],[12,13,14,15,16],[11,17,18,19,20],[10,9,8,7,6]] 输入: 16 解释: 0 1 2 3 4 24 23 22 21 5 12 13 14 15 16 11 17 18 19 20 10 9 8 7 6

最终的路线用加粗进行了标记。 我们必须等到时间为 16,此时才能保证平台 (0, 0) 和 (4, 4) 是连通的 提示:

2 <= N <= 50. grid[i][j] 位于区间 [0, ..., N*N - 1] 内。

class Solution {
  public int swimInWater(int[][] grid) {
        int N = grid.length;
        int lo = Math.max(grid[0][0], grid[N - 1][N -1]);
        int hi = N * N - 1;
        BitSet bs = new BitSet(hi);
        while (lo < hi) {
            int mi = (lo + hi) >> 1;
            if (dfs(grid, 0, 0, mi, bs)) hi = mi;
            else lo = mi + 1;
            bs.clear();
        }
        return lo;
    }

    public boolean dfs (int[][] grid, int i, int j, int limit, BitSet bs) {
        if (bs.get(i * grid.length + j) || grid[i][j] > limit) return false;
        if (i == grid.length - 1 && j == grid.length - 1) return true;
        bs.set(i * grid.length + j);
        if (i < grid.length - 1 && dfs(grid, i + 1, j, limit, bs)) return true;
        if (j < grid.length - 1 && dfs(grid, i, j + 1, limit, bs)) return true;
        if (i > 0 && dfs(grid, i - 1, j, limit, bs)) return true;
        if (j > 0 && dfs(grid, i, j - 1, limit, bs)) return true;
        return false;
    }
}

779. 第K个语法符号

在第一行我们写上一个 0。接下来的每一行,将前一行中的0替换为01,1替换为10。

给定行数 N 和序数 K,返回第 N 行中第 K个字符。(K从1开始)

例子:

输入: N = 1, K = 1 输出: 0

输入: N = 2, K = 1 输出: 0

输入: N = 2, K = 2 输出: 1

输入: N = 4, K = 5 输出: 1

解释: 第一行: 0 第二行: 01 第三行: 0110 第四行: 01101001

注意:

N 的范围 [1, 30]. K 的范围 [1, 2^(N-1)].

class Solution {
     public int kthGrammar(int N, int K) {
         //我这里k-1了,所以与题意正好相反了
        return recursion(N, K - 1);
    }
    
    public int recursion(int N, int k){
        if(N == 1)  return 0;
        int res = 0;
        //首先要得到前N - 1排的那个数字
        int num = recursion(N - 1, k / 2);
        if(k % 2 == 0){
            if(num == 1)    res = 1;
            else    res = 0;
        }else{
            if(num == 1)    res = 0;
            else    res = 1;
        }
        return res;
    }
}

780. 到达终点

从点 (x, y) 可以转换到 (x, x+y) 或者 (x+y, y)。

给定一个起点 (sx, sy) 和一个终点 (tx, ty),如果通过一系列的转换可以从起点到达终点,则返回 True ,否则返回 False。

示例: 输入: sx = 1, sy = 1, tx = 3, ty = 5 输出: True 解释: 可以通过以下一系列转换从起点转换到终点: (1, 1) -> (1, 2) (1, 2) -> (3, 2) (3, 2) -> (3, 5)

输入: sx = 1, sy = 1, tx = 2, ty = 2 输出: False

输入: sx = 1, sy = 1, tx = 1, ty = 1 输出: True

注意:

sx, sy, tx, ty 是范围在 [1, 10^9] 的整数。

PS: 正规套路应该是递归 在这里插入图片描述

但是!!! 他这个题不正规,所以只能反过来想 从结束点找出发点

class Solution {
     public boolean reachingPoints(int sx, int sy, int tx, int ty) {
        while (tx >= sx && ty >= sy) {
            if (tx == ty) break;
            //如果tx>ty  只能是(x+y,y)过来的
            if (tx > ty) {
            	//如果结束点的y大于出发点的y
            	//tx是x+y来的,所以%y,就是求出原x
                if (ty > sy) tx %= ty;
                //反过来的话,起始点和结束点的y已经一致了
                //只需要看加上的是不是y的倍数
                //tx-sx这里就是n多个y,看他%y是不是能全部%掉
                else return (tx - sx) % ty == 0;
            } else {
                if (tx > sx) ty %= tx;
                else return (ty - sy) % tx == 0;
            }
        }
        return (tx == sx && ty == sy);
    }
 
 
}