1001.照明网格、413.最长回文子串、5.等差数列划分——leetcode刷题第28天

这篇博客探讨了两个编程问题:如何在网格上进行照明查询并保持状态更新,以及如何找出一个整数数组中所有等差子序列的数量。网格照明问题涉及建立映射表来跟踪灯的状态,并在每次查询后更新;等差数列划分问题通过贪心策略解决,按公差找到所有等差子序列。这两个问题都展示了在算法设计中对状态管理和数学理解的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第二十八天

1001 网格照明

在大小为 n x n 的网格 grid 上,每个单元格都有一盏灯,最初灯都处于 关闭 状态。

给你一个由灯的位置组成的二维数组 lamps ,其中 lamps[i] = [rowi, coli] 表示打开 位于 grid[rowi][coli] 的灯。即便同一盏灯可能在 lamps 中多次列出,不会影响这盏灯处于 打开 状态。

当一盏灯处于打开状态,它将会照亮 自身所在单元格 以及同一 、同一 和两条对角线 上的 所有其他单元格

另给你一个二维数组 queries ,其中 queries[j] = [rowj, colj] 。对于第 j 个查询,如果单元格 [rowj, colj] 是被照亮的,则查询结果为 1 ,否则为 0 。在第 j 次查询之后 [按照查询的顺序] ,关闭 位于单元格 grid[rowj][colj] 上及相邻8 个方向上(与单元格 grid[rowi][coli] 共享角或边)的任何灯。

返回一个整数数组 ans 作为答案, ans[j] 应等于第 j 次查询 queries[j] 的结果,1 表示照亮,0 表示未照亮。

方法 线映射

建立四个映射表,分别用于记录x轴方向上点亮的灯的数量,y轴方向上点亮的灯的数量,正对角线上点亮的灯的数量,以及反对角线上点亮的灯的数量。由于同一盏灯可能会被多次点亮(即在lambs数组中存在重复的元素)所以我们还需要一个哈希表用来记录所有出现过的灯的位置。

开始时,更新好所有的映射表和哈希表,每一次询问,只需要访问一次四个映射表中对应元素的数量是否大于1即可,否则返回0,询问完成之后,如果当前询问的点的九宫格内,存在主动点亮的光源,那我们就将这个光源熄灭,从哈希表中删除,同时要更新四个映射表即可。

关于正对角线和反对角线的映射,容易知道,所有在同一条正对角线上的点,它们之间的坐标(x1,y1)(x2,y2)满足下列关系
x1+y1=x2+y2 x_1+y_1=x_2+y_2 x1+y1=x2+y2
因此,对于正对角线上的点,我们可以通过x+y来唯一标识一条对角线,同理我们可以用x-y或者y-x来唯一标识一条反对角线。

class Solution {
    public int[] dx = {0, 0, 0, 1, -1, 1, 1, -1, -1};
    public int[] dy = {0, 1, -1, 0, 0, 1, -1, 1, -1};

    public int[] gridIllumination(int n, int[][] lamps, int[][] queries) {
        int[] ans = new int[queries.length];
        Map<Integer, Integer> xAxis = new HashMap<>();
        Map<Integer, Integer> yAxis = new HashMap<>();
        Map<Integer, Integer> xy = new HashMap<>();
        Map<Integer, Integer> yx = new HashMap<>();
        Set<Point> set = new HashSet<>();
        Point tempPoint = new Point(-1, -1);
        for (int[] lamp : lamps){
            tempPoint.x = lamp[0];
            tempPoint.y = lamp[1];
            if (!set.contains(tempPoint)){
                xAxis.put(lamp[0], xAxis.getOrDefault(lamp[0], 0) + 1);
                yAxis.put(lamp[1], yAxis.getOrDefault(lamp[1], 0) + 1);
                xy.put(lamp[0] + lamp[1], xy.getOrDefault(lamp[0] + lamp[1], 0) + 1);
                yx.put(lamp[0] - lamp[1], yx.getOrDefault(lamp[0] - lamp[1], 0) + 1);
                set.add(new Point(lamp[0], lamp[1]));
            }
        }
        int index = 0;  
        for (int[] query : queries){
            if (xAxis.getOrDefault(query[0], 0) > 0
            || yAxis.getOrDefault(query[1], 0) > 0
            || xy.getOrDefault(query[0] + query[1], 0) > 0
            || yx.getOrDefault(query[0] - query[1], 0) > 0){
                ans[index] = 1;
            }
            for (int i = 0; i < 9; ++i){
                if (query[0] + dx[i] >= 0 && query[0] + dx[i] < n 
                && query[1] + dy[i] >= 0 && query[1] + dy[i] < n){
                    tempPoint.x = query[0] + dx[i];
                    tempPoint.y = query[1] + dy[i];
                    if (set.contains(tempPoint)){
                        set.remove(tempPoint);
                        xAxis.put(tempPoint.x, xAxis.get(tempPoint.x) - 1);
                        yAxis.put(tempPoint.y, yAxis.get(tempPoint.y) - 1);
                        xy.put(tempPoint.x + tempPoint.y, xy.get(tempPoint.x + tempPoint.y) - 1);
                        yx.put(tempPoint.x - tempPoint.y, yx.get(tempPoint.x - tempPoint.y) - 1);
                    }
                }
            }
            index++;
        }
        return ans;
    }
}

class Point{
    int x;
    int y;
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object p){
        Point cmp = (Point) p;
        return cmp.x == x && cmp.y == y;
    }

    @Override
    public int hashCode(){
        return x | y;
    }
}

5 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

方法 动态规划

考虑回文串的定义,对于一个字符串,如果和其反转字符串相同,那么这个字符串就是一个回文串。

依据回文串的定义,容易知道:

如果字符x和字符y相同,并且字符串S是一个回文字符串,那么xSy也一定是一个回文字符串。

利用上述性质,我们定义isPalindrome[i][j]表示从ij位置的子串是否为回文字符串。容易得出以下状态转移方程:
isPalindrome[i][j]=isPalindrome[i+1][j−1],ifs(i)==s(j) isPalindrome[i][j]=isPalindrome[i+1][j-1],if \quad s(i)==s(j) isPalindrome[i][j]=isPalindrome[i+1][j1],ifs(i)==s(j)
即如果i+1,j-1位置上子串是一个回文字符串,并且ij位置上的字符相同,那么从ij位置的子串一定也是一个回文字符串。

初始条件如下:
isPalindrome[i][i]=trueisPalindrome[i][i+1]=trueifs(i)==s(i+1) isPalindrome[i][i]=true\\ isPalindrome[i][i+1]=true \quad if \quad s(i)==s(i+1) isPalindrome[i][i]=trueisPalindrome[i][i+1]=trueifs(i)==s(i+1)
我们以长度为循环变量,初始长度为2,遍历所有长度即可。

class Solution {
    public String longestPalindrome(String s) {
        if (s.length() == 1) return s;
        boolean[][] isPalindrome = new boolean[s.length()][s.length()];
        for (int i = 0; i < s.length(); ++i) isPalindrome[i][i] = true;
        int maxL = 1;
        for (int length = 2; length <= s.length(); ++length){
            for (int start = 0; start <= s.length() - length; ++start){
                int end = start + length - 1;
                if (start + 1 < s.length()){
                    if (s.charAt(start) == s.charAt(end)) {
                        isPalindrome[start][end] = end - start + 1 == 2 ? true : isPalindrome[start + 1][end - 1];
                        }
                    if (isPalindrome[start][end]) maxL = Math.max(maxL, length);
                }
            }
        }
        for (int i = 0; i <= s.length() - maxL; ++i){
            if (isPalindrome[i][i + maxL - 1]) return s.substring(i, i + maxL);
        }
        return s.substring(0, 1);
    }
}

413 等差数列划分

如果一个数列 至少有三个元素 ,并且任意两个相邻元素之差相同,则称该数列为等差数列。

  • 例如,[1,3,5,7,9][7,7,7,7][3,-1,-5,-9] 都是等差数列。

给你一个整数数组 nums ,返回数组 nums 中所有为等差数组的 子数组 个数。

子数组 是数组中的一个连续序列。

方法 贪心

依据等差数列和子数组的特性,我们只需要依次求出公差d,然后从头往后一直找到第一个使得公差不为d的位置end,此时我们更新答案,直到真个数列被遍历完成即可。

class Solution {
    public int numberOfArithmeticSlices(int[] nums) {
        if (nums.length < 3) return 0;
        int ans = 0, start = 0, end = 0;
        while (start < nums.length - 2){
            int d = nums[start + 1] - nums[start];
            end = start + 1;
            while (end < nums.length - 1) {
                if (d == nums[end + 1] - nums[end]){
                    end++;
                }
                else break;
            }
            if (end - start + 1 >= 3){
                int n = end - start -1;
                ans += (1 + n) * n / 2;
            }
            start = end;
        }
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值