3461.判断操作后字符串中的数字是否相等I

目标

给你一个由数字组成的字符串 s 。重复执行以下操作,直到字符串恰好包含 两个 数字:

  • 从第一个数字开始,对于 s 中的每一对连续数字,计算这两个数字的和 模 10。
  • 用计算得到的新数字依次替换 s 的每一个字符,并保持原本的顺序。

如果 s 最后剩下的两个数字 相同 ,返回 true 。否则,返回 false。

示例 1:

输入: s = "3902"
输出: true
解释:
一开始,s = "3902"
第一次操作:
(s[0] + s[1]) % 10 = (3 + 9) % 10 = 2
(s[1] + s[2]) % 10 = (9 + 0) % 10 = 9
(s[2] + s[3]) % 10 = (0 + 2) % 10 = 2
s 变为 "292"
第二次操作:
(s[0] + s[1]) % 10 = (2 + 9) % 10 = 1
(s[1] + s[2]) % 10 = (9 + 2) % 10 = 1
s 变为 "11"
由于 "11" 中的数字相同,输出为 true。

示例 2:

输入: s = "34789"
输出: false
解释:
一开始,s = "34789"。
第一次操作后,s = "7157"。
第二次操作后,s = "862"。
第三次操作后,s = "48"。
由于 '4' != '8',输出为 false。

说明:

  • 3 <= s.length <= 100
  • s 仅由数字组成。

思路

有一个长度为 n 的数字字符串,每一次操作收集相邻两个数字之和模 10 的结果,得到一个长度为 n - 1 的字符串,反复执行操作直到剩下两个数字,判断剩余的两个数字是否相等。

简单的做法就是根据题意模拟。

n - 2 次合并后,s[i] 对最终结果的贡献等于从它开始,往下 n - 2 层到达根(可以看成一个倒着的杨辉三角)的路径数。

代码


/**
 * @date 2025-10-23 9:40
 */
public class HasSameDigits3461 {

    /**
     * 计算二项式展开系数
     */
    public boolean hasSameDigits_v1(String s) {
        int n = s.length();
        BigInteger[] factor = getFactor(n - 2);
        BigInteger a = BigInteger.ZERO;
        BigInteger b = BigInteger.ZERO;
        for (int i = 0; i < n - 1; i++) {
            a = a.add(BigInteger.valueOf(s.charAt(i) - '0').multiply(factor[i]));
        }
        for (int i = 1; i < n; i++) {
            b = b.add(BigInteger.valueOf(s.charAt(i) - '0').multiply(factor[i - 1]));
        }
        return a.mod(BigInteger.TEN).equals(b.mod(BigInteger.TEN));
    }

    public static Map<Integer, BigInteger[]> cache = new HashMap<>();

    public static BigInteger[] getFactor(int n) {
        BigInteger[] c = cache.get(n);
        if (c != null) {
            return c;
        }
        BigInteger[] prev = new BigInteger[1];
        prev[0] = BigInteger.ONE;
        for (int i = 1; i <= n; i++) {
            BigInteger[] cur = new BigInteger[i + 1];
            cur[0] = BigInteger.ONE;
            cur[i] = BigInteger.ONE;
            for (int j = 1; j < i; j++) {
                cur[j] = prev[j - 1].add(prev[j]);
            }
            prev = cur;
        }
        return prev;
    }

    /**
     * 模拟
     */
    public boolean hasSameDigits(String s) {
        Queue<Integer> q = new ArrayDeque<>();
        int n = s.length();
        for (int i = 0; i < n; i++) {
            q.offer(s.charAt(i) - '0');
        }
        while (q.size() > 2) {
            int len = q.size();
            int prev = q.poll();
            for (int i = 1; i < len; i++) {
                int cur = q.poll();
                q.offer((prev + cur) % 10);
                prev = cur;
            }
        }
        int a = q.poll();
        int b = q.poll();
        return a == b;
    }

}

性能

2011.执行操作后的变量值

目标

存在一种仅支持 4 种操作和 1 个变量 X 的编程语言:

  • ++X 和 X++ 使变量 X 的值 加 1
  • --X 和 X-- 使变量 X 的值 减 1

最初,X 的值是 0

给你一个字符串数组 operations ,这是由操作组成的一个列表,返回执行所有操作后, X 的 最终值 。

示例 1:

输入:operations = ["--X","X++","X++"]
输出:1
解释:操作按下述步骤执行:
最初,X = 0
--X:X 减 1 ,X =  0 - 1 = -1
X++:X 加 1 ,X = -1 + 1 =  0
X++:X 加 1 ,X =  0 + 1 =  1

示例 2:

输入:operations = ["++X","++X","X++"]
输出:3
解释:操作按下述步骤执行: 
最初,X = 0
++X:X 加 1 ,X = 0 + 1 = 1
++X:X 加 1 ,X = 1 + 1 = 2
X++:X 加 1 ,X = 2 + 1 = 3

示例 3:

输入:operations = ["X++","++X","--X","X--"]
输出:0
解释:操作按下述步骤执行:
最初,X = 0
X++:X 加 1 ,X = 0 + 1 = 1
++X:X 加 1 ,X = 1 + 1 = 2
--X:X 减 1 ,X = 2 - 1 = 1
X--:X 减 1 ,X = 1 - 1 = 0

说明:

  • 1 <= operations.length <= 100
  • operations[i] 将会是 "++X"、"X++"、"--X" 或 "X--"

思路

有一个字符串数组,其元素取自 ++x、x++、--x、x--x 初值的为 0,求操作后 x 的值。

代码


/**
 * @date 2025-10-20 8:43
 */
public class FinalValueAfterOperations2011 {

    public int finalValueAfterOperations(String[] operations) {
        int res = 0;
        for (String operation : operations) {
            if (operation.charAt(0) == '+' || operation.charAt(operation.length() - 1) == '+') {
                res++;
            }else {
                res--;
            }
        }
        return res;
    }
}

性能

3349.检测相邻递增子数组I

目标

给你一个由 n 个整数组成的数组 nums 和一个整数 k,请你确定是否存在 两个 相邻 且长度为 k 的 严格递增 子数组。具体来说,需要检查是否存在从下标 a 和 b (a < b) 开始的 两个 子数组,并满足下述全部条件:

  • 这两个子数组 nums[a..a + k - 1] 和 nums[b..b + k - 1] 都是 严格递增 的。
  • 这两个子数组必须是 相邻的,即 b = a + k。

如果可以找到这样的 两个 子数组,请返回 true;否则返回 false。

子数组 是数组中的一个连续 非空 的元素序列。

示例 1:

输入:nums = [2,5,7,8,9,2,3,4,3,1], k = 3
输出:true
解释:
从下标 2 开始的子数组为 [7, 8, 9],它是严格递增的。
从下标 5 开始的子数组为 [2, 3, 4],它也是严格递增的。
两个子数组是相邻的,因此结果为 true。

示例 2:

输入:nums = [1,2,3,4,4,4,4,5,6,7], k = 5
输出:false

说明:

  • 2 <= nums.length <= 100
  • 1 <= 2 * k <= nums.length
  • -1000 <= nums[i] <= 1000

思路

判断是否存在两个长度为 k 的相邻的子数组,要求子数组内严格递增。

定义 dp[i] 表示以 i 结尾的递增子数组的最大长度,如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1,否则 dp[i] = 1。最后只需判断是否存在 i 使得 dp[i] >= k && dp[i + k] >= k 即可。

代码


/**
 * @date 2025-10-14 9:02
 */
public class HasIncreasingSubarrays3349 {

    public boolean hasIncreasingSubarrays(List<Integer> nums, int k) {
        int n = nums.size();
        int[] dp = new int[n];
        dp[0] = 1;
        for (int i = 1; i < n; i++) {
            if (nums.get(i) > nums.get(i - 1)) {
                dp[i] = dp[i - 1] + 1;
            } else {
                dp[i] = 1;
            }
        }
        for (int i = k - 1; i + k < n; i++) {
            if (dp[i] >= k && dp[i + k] >= k) {
                return true;
            }
        }
        return false;
    }

}

性能

2273.移除字母异位词后的结果数组

目标

给你一个下标从 0 开始的字符串 words ,其中 words[i] 由小写英文字符组成。

在一步操作中,需要选出任一下标 i ,从 words 中 删除 words[i] 。其中下标 i 需要同时满足下述两个条件:

  1. 0 < i < words.length
  2. words[i - 1] 和 words[i] 是 字母异位词 。

只要可以选出满足条件的下标,就一直执行这个操作。

在执行所有操作后,返回 words 。可以证明,按任意顺序为每步操作选择下标都会得到相同的结果。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。例如,"dacb" 是 "abdc" 的一个字母异位词。

示例 1:

输入:words = ["abba","baba","bbaa","cd","cd"]
输出:["abba","cd"]
解释:
获取结果数组的方法之一是执行下述步骤:
- 由于 words[2] = "bbaa" 和 words[1] = "baba" 是字母异位词,选择下标 2 并删除 words[2] 。
  现在 words = ["abba","baba","cd","cd"] 。
- 由于 words[1] = "baba" 和 words[0] = "abba" 是字母异位词,选择下标 1 并删除 words[1] 。
  现在 words = ["abba","cd","cd"] 。
- 由于 words[2] = "cd" 和 words[1] = "cd" 是字母异位词,选择下标 2 并删除 words[2] 。
  现在 words = ["abba","cd"] 。
无法再执行任何操作,所以 ["abba","cd"] 是最终答案。

示例 2:

输入:words = ["a","b","c","d","e"]
输出:["a","b","c","d","e"]
解释:
words 中不存在互为字母异位词的两个相邻字符串,所以无需执行任何操作。

说明:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 10
  • words[i] 由小写英文字母组成

思路

定义字母异位词是字母构成完全相同的单词,有一个单词列表,如果相邻的单词是字母异位词,仅保留第一个,删除剩余字母异位词,返回最终结果数组。

判断是否是异位词可以将每个单词的字符排序后进行比较,根据题意仅保留第一个单词即可。

代码


/**
 * @date 2025-10-13 8:51
 */
public class RemoveAnagrams2273 {

    public List<String> removeAnagrams(String[] words) {
        int n = words.length;
        String[] tmp = new String[n];
        for (int i = 0; i < n; i++) {
            char[] chars = words[i].toCharArray();
            Arrays.sort(chars);
            tmp[i] = new String(chars);
        }
        List<String> res = new ArrayList<>();
        res.add(words[0]);
        for (int i = 1; i < n; i++) {
            if (tmp[i].equals(tmp[i - 1])) {
                continue;
            }
            res.add(words[i]);
        }
        return res;
    }

}

性能

1518.换水问题

目标

超市正在促销,你可以用 numExchange 个空水瓶从超市兑换一瓶水。最开始,你一共购入了 numBottles 瓶水。

如果喝掉了水瓶中的水,那么水瓶就会变成空的。

给你两个整数 numBottles 和 numExchange ,返回你 最多 可以喝到多少瓶水。

示例 1:

输入:numBottles = 9, numExchange = 3
输出:13
解释:你可以用 3 个空瓶兑换 1 瓶水。
所以最多能喝到 9 + 3 + 1 = 13 瓶水。

示例 2:

输入:numBottles = 15, numExchange = 4
输出:19
解释:你可以用 4 个空瓶兑换 1 瓶水。
所以最多能喝到 15 + 3 + 1 = 19 瓶水。

提示:

  • 1 <= numBottles <= 100
  • 2 <= numExchange <= 100

思路

有 numBottles 瓶水,numExchange 个空瓶可以兑换一瓶水,问最多可以喝几瓶水。

依题意模拟即可。空瓶数 = 剩余未兑换空瓶 + 新兑换瓶数。

代码


/**
 * @date 2025-10-01 19:05
 */
public class NumWaterBottles1518 {

    public int numWaterBottles(int numBottles, int numExchange) {
        int res = numBottles;
        while (numBottles >= numExchange) {
            int change = numBottles / numExchange;
            res += change;
            numBottles = numBottles % numExchange + change;
        }
        return res;
    }
}

性能

976.三角形的最大周长

目标

给定由一些正数(代表长度)组成的数组 nums ,返回 由其中三个长度组成的、面积不为零的三角形的最大周长 。如果不能形成任何面积不为零的三角形,返回 0。

示例 1:

输入:nums = [2,1,2]
输出:5
解释:你可以用三个边长组成一个三角形:1 2 2。

示例 2:

输入:nums = [1,2,1,10]
输出:0
解释:
你不能用边长 1,1,2 来组成三角形。
不能用边长 1,1,10 来构成三角形。
不能用边长 1、2 和 10 来构成三角形。
因为我们不能用任何三条边长来构成一个非零面积的三角形,所以我们返回 0。

说明:

  • 3 <= nums.length <= 10^4
  • 1 <= nums[i] <= 10^6

思路

已知正整数数组 nums,从中取出三个元素作为三角形的边长,求三角形的最大周长。

最大周长一定是长度最长的三条边,只需要判断能否组成三角形即可。如果不能,说明当前最大值无法组成三角形,将其移除后按同样的逻辑继续判断即可。

代码


/**
 * @date 2025-09-28 8:50
 */
public class LargestPerimeter976 {

    public int largestPerimeter(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        for (int i = n - 1; i >= 2; i--) {
            if (nums[i] < nums[i - 1] + nums[i - 2]) {
                return nums[i] + nums[i - 1] + nums[i - 2];
            }
        }
        return 0;
    }

}

性能

812.最大三角形面积

目标

给你一个由 X-Y 平面上的点组成的数组 points ,其中 points[i] = [xi, yi] 。从其中取任意三个不同的点组成三角形,返回能组成的最大三角形的面积。与真实值误差在 10^-5 内的答案将会视为正确答案。

示例 1:

输入:points = [[0,0],[0,1],[1,0],[0,2],[2,0]]
输出:2.00000
解释:输入中的 5 个点如上图所示,红色的三角形面积最大。

示例 2:

输入:points = [[1,0],[0,0],[0,1]]
输出:0.50000

提示:

  • 3 <= points.length <= 50
  • -50 <= xi, yi <= 50
  • 给出的所有点 互不相同

思路

暴力解,三层循环,三角形面积可以使用向量的叉积计算。

向量的叉积表示这两个向量构成的平行四边形面积,除以 2 就是三角形的面积。

向量 (x1, y1)(x2, y2) 的叉积等于 |x1 * y2 - x2 * y1|

代码


/**
 * @date 2025-09-27 21:35
 */
public class LargestTriangleArea812 {

    public double largestTriangleArea(int[][] points) {
        int n = points.length;
        double res = 0.0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                for (int k = j + 1; k < n; k++) {
                    int p1 = points[i][0];
                    int q1 = points[i][1];
                    int p2 = points[j][0];
                    int q2 = points[j][1];
                    int p3 = points[k][0];
                    int q3 = points[k][1];
                    res = Math.max(res, Math.abs((p2 - p1) * (q3 - q1) - (p3 - p1) * (q2 - q1)));
                }
            }
        }
        return res / 2;
    }
}

性能

3005.最大频率元素计数

目标

给你一个由 正整数 组成的数组 nums 。

返回数组 nums 中所有具有 最大 频率的元素的 总频率 。

元素的 频率 是指该元素在数组中出现的次数。

示例 1:

输入:nums = [1,2,2,3,1,4]
输出:4
解释:元素 1 和 2 的频率为 2 ,是数组中的最大频率。
因此具有最大频率的元素在数组中的数量是 4 。

示例 2:

输入:nums = [1,2,3,4,5]
输出:5
解释:数组中的所有元素的频率都为 1 ,是最大频率。
因此具有最大频率的元素在数组中的数量是 5 。

说明:

1 <= nums.length <= 100
1 <= nums[i] <= 100

思路

求正整数数组 nums 的最大频率之和。

针对元素计数,如果出现次数等于最大频率则累加最大频率,如果出现新的最大频率则需要将结果重置为最大频率。

代码


/**
 * @date 2025-09-22 8:48
 */
public class MaxFrequencyElements3005 {

    public int maxFrequencyElements_v1(int[] nums) {
        int res = 0;
        int[] cnt = new int[101];
        int max = 0;
        for (int num : nums) {
            cnt[num]++;
            if (max < cnt[num]) {
                max = cnt[num];
                res = cnt[num];
            } else if (max == cnt[num]) {
                res += cnt[num];
            }
        }
        return res;
    }

}

性能

1935.可以输入的最大单词数

目标

键盘出现了一些故障,有些字母键无法正常工作。而键盘上所有其他键都能够正常工作。

给你一个由若干单词组成的字符串 text ,单词间由单个空格组成(不含前导和尾随空格);另有一个字符串 brokenLetters ,由所有已损坏的不同字母键组成,返回你可以使用此键盘完全输入的 text 中单词的数目。

示例 1:

输入:text = "hello world", brokenLetters = "ad"
输出:1
解释:无法输入 "world" ,因为字母键 'd' 已损坏。

示例 2:

输入:text = "leet code", brokenLetters = "lt"
输出:1
解释:无法输入 "leet" ,因为字母键 'l' 和 't' 已损坏。

示例 3:

输入:text = "leet code", brokenLetters = "e"
输出:0
解释:无法输入任何单词,因为字母键 'e' 已损坏。

说明:

  • 1 <= text.length <= 10^4
  • 0 <= brokenLetters.length <= 26
  • text 由若干用单个空格分隔的单词组成,且不含任何前导和尾随空格
  • 每个单词仅由小写英文字母组成
  • brokenLetters 由 互不相同 的小写英文字母组成

思路

有一个字符串,其中的单词由空格分隔,现在键盘有一些按键不能正常工作,返回能够正常输入的单词个数。

使用哈希表记录不正常的按键,分割单词,判断单词是否包含不正常字符。

代码


/**
 * @date 2025-09-15 8:46
 */
public class CanBeTypedWords1935 {

    public int canBeTypedWords(String text, String brokenLetters) {
        Set<Character> brokenSet = new HashSet<>();
        for (char c : brokenLetters.toCharArray()) {
            brokenSet.add(c);
        }
        String[] words = text.split(" ");
        int res = words.length;
        for (String word : words) {
            for (char c : word.toCharArray()) {
                if (brokenSet.contains(c)){
                    res--;
                    break;
                }
            }
        }
        return res;
    }
}

性能

3541.找到频率最高的元音和辅音

目标

给你一个由小写英文字母('a' 到 'z')组成的字符串 s。你的任务是找出出现频率 最高 的元音('a'、'e'、'i'、'o'、'u' 中的一个)和出现频率最高的辅音(除元音以外的所有字母),并返回这两个频率之和。

注意:如果有多个元音或辅音具有相同的最高频率,可以任选其中一个。如果字符串中没有元音或没有辅音,则其频率视为 0。

一个字母 x 的 频率 是它在字符串中出现的次数。

示例 1:

输入: s = "successes"
输出: 6
解释:
元音有:'u' 出现 1 次,'e' 出现 2 次。最大元音频率 = 2。
辅音有:'s' 出现 4 次,'c' 出现 2 次。最大辅音频率 = 4。
输出为 2 + 4 = 6。

示例 2:

输入: s = "aeiaeia"
输出: 3
解释:
元音有:'a' 出现 3 次,'e' 出现 2 次,'i' 出现 2 次。最大元音频率 = 3。
s 中没有辅音。因此,最大辅音频率 = 0。
输出为 3 + 0 = 3。

说明:

  • 1 <= s.length <= 100
  • s 只包含小写英文字母

思路

计算字符串中辅音字母与元音字母的最高频率之和。

代码


/**
 * @date 2025-09-13 22:46
 */
public class MaxFreqSum3541 {

    public int maxFreqSum(String s) {
        int[] cnt = new int[26];
        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for (char c : s.toCharArray()) {
            int i = c - 'a';
            if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
                cnt[i]--;
                min = Math.min(min, cnt[i]);
            } else {
                cnt[i]++;
                max = Math.max(max, cnt[i]);
            }
        }
        return Math.max(0, max) - Math.min(0, min);
    }
}

性能