3517.最小回文排列I

目标

给你一个 回文 字符串 s。

返回 s 的按字典序排列的 最小 回文排列。

如果一个字符串从前往后和从后往前读都相同,那么这个字符串是一个 回文 字符串。

排列 是字符串中所有字符的重排。

如果字符串 a 按字典序小于字符串 b,则表示在第一个不同的位置,a 中的字符比 b 中的对应字符在字母表中更靠前。

如果在前 min(a.length, b.length) 个字符中没有区别,则较短的字符串按字典序更小。

示例 1:

输入: s = "z"
输出: "z"
解释:
仅由一个字符组成的字符串已经是按字典序最小的回文。

示例 2:

输入: s = "babab"
输出: "abbba"
解释:
通过重排 "babab" → "abbba",可以得到按字典序最小的回文。

示例 3:

输入: s = "daccad"
输出: "acddca"
解释:
通过重排 "daccad" → "acddca",可以得到按字典序最小的回文。

说明:

  • 1 <= s.length <= 10^5
  • s 由小写英文字母组成。
  • 保证 s 是回文字符串。

思路

有一个回文字符串 s,将其重新排列成回文字符串,使得字典序最小。

记录字符串中字符的出现次数,然后按字典序从两边向中间填充,如果出现次数为奇数,那么该字符为中间元素。

代码


/**
 * @date 2026-07-28 9:03
 */
public class SmallestPalindrome3517 {

    public String smallestPalindrome(String s) {
        int[] cnt = new int[26];
        int n = s.length();
        char[] res = new char[n];
        for (char c : s.toCharArray()) {
            cnt[c - 'a']++;
        }
        char mid = 0;
        int cur = 0;
        for (int i = 0; i < 26; i++) {
            if (cnt[i] % 2 == 1) {
                mid = (char) ('a' + i);
            }
            int k = cnt[i] / 2;
            char c = (char) ('a' + i);
            for (int j = 0; j < k; j++) {
                res[cur + j] = c;
                res[n - cur - j - 1] = c;
            }
            cur += k;
        }
        if (n % 2 == 1) {
            res[n / 2] = mid;
        }
        return new String(res);
    }

}

性能

1291.顺次数

目标

我们定义「顺次数」为:每一位上的数字都比前一位上的数字大 1 的整数。

请你返回由 [low, high] 范围内所有顺次数组成的 有序 列表(从小到大排序)。

示例 1:

输出:low = 100, high = 300
输出:[123,234]

示例 2:

输出:low = 1000, high = 13000
输出:[1234,2345,3456,4567,5678,6789,12345]

说明:

  • 10 <= low <= high <= 10^9

思路

定义顺序数字为从左到右每一位都比前一位的数字大 1 的整数。返回 [low, high] 范围内的所有顺序数字,按从小到大返回。

枚举数字长度以及开头数字,判断生成的顺序数字是否在 [low, high] 内即可。

代码


/**
 * @date 2026-07-13 9:10
 */
public class SequentialDigits1291 {

    public List<Integer> sequentialDigits(int low, int high) {
        int l = Integer.toString(low).length();
        int r = Integer.toString(high).length();
        List<Integer> res = new ArrayList<>();
        for (int i = l; i <= r; i++) {
            for(int j = 1; j + i <= 10; j++){
                Integer num = genInteger(j, i);
                if (low <= num && num <= high){
                    res.add(num);
                }
            }
        }
        return res;
    }

    public Integer genInteger(int first, int length) {
        int res = 0;
        for (int i = 0; i < length && first < 10; i++) {
            res = res * 10 + first;
            first++;
        }
        return res;
    }

}

性能

3756.连接非零数字并乘以其数字和II

目标

给你一个长度为 m 的字符串 s,其中仅包含数字。另给你一个二维整数数组 queries,其中 queries[i] = [li, ri]。

对于每个 queries[i],提取 子串 s[li..ri],然后执行以下操作:

  • 将子串中所有 非零数字 按照原始顺序连接起来,形成一个新的整数 x。如果没有非零数字,则 x = 0。
  • 令 sum 为 x 中所有数字的 数字和 。答案为 x * sum。

返回一个整数数组 answer,其中 answer[i] 是第 i 个查询的答案。

由于答案可能非常大,请返回其对 10^9 + 7 取余数的结果。

子串 是字符串中的一个连续、非空 字符序列。

示例 1:

输入: s = "10203004", queries = [[0,7],[1,3],[4,6]]
输出: [12340, 4, 9]
解释:
s[0..7] = "10203004"
    x = 1234
    sum = 1 + 2 + 3 + 4 = 10
    因此,答案是 1234 * 10 = 12340。
s[1..3] = "020"
    x = 2
    sum = 2
    因此,答案是 2 * 2 = 4。
s[4..6] = "300"
    x = 3
    sum = 3
    因此,答案是 3 * 3 = 9。

示例 2:

输入: s = "1000", queries = [[0,3],[1,1]]
输出: [1, 0]
解释:
s[0..3] = "1000"
    x = 1
    sum = 1
    因此,答案是 1 * 1 = 1。
s[1..1] = "0"
    x = 0
    sum = 0
    因此,答案是 0 * 0 = 0。

示例 3:

输入: s = "9876543210", queries = [[0,9]]
输出: [444444137]
解释:
s[0..9] = "9876543210"
    x = 987654321
    sum = 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 = 45
    因此,答案是 987654321 * 45 = 44444444445。
    返回结果为 44444444445 mod (10^9 + 7) = 444444137。

说明:

  • 1 <= m == s.length <= 10^5
  • s 仅由数字组成。
  • 1 <= queries.length <= 10^5
  • queries[i] = [li, ri]
  • 0 <= li <= ri < m

思路

有一个数字字符串 s,针对每一个子串 s[queries[i][0], queries[i][1]],返回其非零数字所表示的数字 乘以 每位数字之和 对 1000000007 取余的结果。

3754.连接非零数字并乘以其数字和I 相比,本题的数字是由 queries 给出的子串,需要返回每一个子串的结果。

数位和可以使用前缀和快速计算。子串非零数字所表示的数字也可以通过前缀计算。

区间 [l, r] 所表示的数字对 MOD 取模的值为 (prefixNum[r + 1] + MOD - prefixNum[l] * base[k] % MOD) % MOD,例如,1230456[2, 4] 中的非零数字所表示的数字是 34,它等于 prefixNum[5]:1234 - prefixNum[2]:12 * 100,其中 100 = 10^kk 表示 [l, r] 中非零数字的个数。

代码


/**
 * @date 2026-07-08 9:50
 */
public class SumAndMultiply3756 {

    public int[] sumAndMultiply(String s, int[][] queries) {
        int n = s.length();
        int[] prefix = new int[n + 1];
        int[] prefixLength = new int[n + 1];
        long[] prefixNum = new long[n + 1];
        for (int i = 0; i < n; i++) {
            int d = s.charAt(i) - '0';
            prefix[i + 1] = prefix[i] + d;
            prefixLength[i + 1] = prefixLength[i] + (d != 0 ? 1 : 0);
            prefixNum[i + 1] = (prefixNum[i] * (d != 0 ? 10 : 1) + d) % MOD;
        }
        int ql = queries.length;
        int[] res = new int[ql];
        for (int i = 0; i < ql; i++) {
            int l = queries[i][0];
            int r = queries[i][1];
            int sum = prefix[r + 1] - prefix[l];
            long x = (prefixNum[r + 1] + MOD - prefixNum[l] * base[prefixLength[r + 1] - prefixLength[l]] % MOD) % MOD;
            res[i] = (int) (x * sum % MOD);
        }
        return res;
    }

}

性能

3754.连接非零数字并乘以其数字和I

目标

给你一个整数 n。

将 n 中所有的 非零数字 按照它们的原始顺序连接起来,形成一个新的整数 x。如果不存在 非零数字 ,则 x = 0。

sum 为 x 中所有数字的 数字和 。

返回一个整数,表示 x * sum 的值。

示例 1:

输入: n = 10203004
输出: 12340
解释:
非零数字是 1、2、3 和 4。因此,x = 1234。
数字和为 sum = 1 + 2 + 3 + 4 = 10。
因此,答案是 x * sum = 1234 * 10 = 12340。

示例 2:

输入: n = 1000
输出: 1
解释:
非零数字是 1,因此 x = 1 且 sum = 1。
因此,答案是 x * sum = 1 * 1 = 1。

说明:

  • 0 <= n <= 10^9

思路

计算整数 n 的所有非零数字之和记为 sum,去掉 n 中的 0 形成的新整数记为 x,返回 x * sum 的值。

依题意模拟即可。

代码


/**
 * @date 2026-07-07 9:13
 */
public class SumAndMultiply3754 {

    public long sumAndMultiply(int n) {
        long sum = 0;
        long x = 0;
        int base = 1;
        while (n > 0) {
            int rem = n % 10;
            sum += rem;
            x += rem * base;
            base *= rem == 0 ? 1 : 10;
            n /= 10;
        }
        return x * sum;
    }
}

性能

1189.”气球”的最大数量

目标

给你一个字符串 text,你需要使用 text 中的字母来拼凑尽可能多的单词 "balloon"(气球)。

字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 "balloon"。

示例 1:

输入:text = "nlaebolko"
输出:1

示例 2:

输入:text = "loonbalxballpoon"
输出:2

示例 3:

输入:text = "leetcode"
输出:0

提示:

  • 1 <= text.length <= 10^4
  • text 全部由小写英文字母组成

注意:本题与 2287.重排字符形成目标字符串 相同。

思路

使用字符串 text 中的字母拼凑单词 balloon,求最多能拼出多少个。

单词由 1a1b1n2l2o 组成。对 text 中的字母计数,统计相关字母频次的最小值即可(lo 需要除以 2)。

代码


/**
 * @date 2026-06-22 9:05
 */
public class MaxNumberOfBalloons1189 {

    public int maxNumberOfBalloons_v1(String text) {
        char[] cnt = new char[26];
        for (char c : text.toCharArray()) {
            cnt[c - 'a']++;
        }
        int res = Integer.MAX_VALUE;
        res = Math.min(res, cnt[0]);
        res = Math.min(res, cnt[1]);
        res = Math.min(res, cnt[11] / 2);
        res = Math.min(res, cnt[13]);
        res = Math.min(res, cnt[14] / 2);
        return res;
    }

}

性能

3612.用特殊操作处理字符串I

目标

给你一个字符串 s,它由小写英文字母和特殊字符:*、# 和 % 组成。

请根据以下规则从左到右处理 s 中的字符,构造一个新的字符串 result:

  • 如果字符是 小写 英文字母,则将其添加到 result 中。
  • 字符 '*' 会 删除 result 中的最后一个字符(如果存在)。
  • 字符 '#' 会 复制 当前的 result 并 追加 到其自身后面。
  • 字符 '%' 会 反转 当前的 result。

在处理完 s 中的所有字符后,返回最终的字符串 result。

示例 1:

输入: s = "a#b%*"
输出: "ba"
解释:
i s[i] 操作 当前 result
0 'a' 添加 'a' "a"
1 '#' 复制 result "aa"
2 'b' 添加 'b' "aab"
3 '%' 反转 result "baa"
4 '*' 删除最后一个字符 "ba"
因此,最终的 result 是 "ba"。

示例 2:

输入: s = "z*#"
输出: ""
解释:
i s[i] 操作 当前 result
0 'z' 添加 'z' "z"
1 '*' 删除最后一个字符 ""
2 '#' 复制字符串 ""
因此,最终的 result 是 ""。

说明:

  • 1 <= s.length <= 20
  • s 只包含小写英文字母和特殊字符 *、# 和 %。

思路

根据规则从左到右处理字符串 s,如果时小写字母添加到结果 result,如果是 * 删除 result 的最后一个字符,如果是 #result 追加到自身的后面,如果是 % 则反转当前的 result

字符串长度最大为 20,可以暴力模拟。

代码


/**
 * @date 2025-07-14 9:15
 */
public class ProcessStrQ1 {

    public String processStr(String s) {
        StringBuilder sb = new StringBuilder();
        int n = s.length();
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            switch (c) {
                case '*':
                    if (sb.length() > 0) {
                        sb.deleteCharAt(sb.length() - 1);
                    }
                    break;
                case '#':
                    sb.append(sb);
                    break;
                case '%':
                    sb.reverse();
                    break;
                default:
                    sb.append(c);
            }
        }
        return sb.toString();
    }

}

性能

3838.带权单词映射

目标

给你一个字符串数组 words,其中每个字符串表示一个由小写英文字母组成的单词。

同时给你一个长度为 26 的整数数组 weights,其中 weights[i] 表示第 i 个小写英文字母的权重。

单词的 权重 定义为其所有字符权重的 总和。

对于每个单词,将其权重对 26 取模,并将结果按字母倒序映射到一个小写英文字母(0 -> 'z', 1 -> 'y', ..., 25 -> 'a')。

返回一个由所有单词映射后的字符按顺序连接而成的字符串。

示例 1:

输入: words = ["abcd","def","xyz"], weights = [5,3,12,14,1,2,3,2,10,6,6,9,7,8,7,10,8,9,6,9,9,8,3,7,7,2]
输出: "rij"
解释:
"abcd" 的权重是 5 + 3 + 12 + 14 = 34。对 26 取模的结果是 34 % 26 = 8,映射为 'r'。
"def" 的权重是 14 + 1 + 2 = 17。对 26 取模的结果是 17 % 26 = 17,映射为 'i'。
"xyz" 的权重是 7 + 7 + 2 = 16。对 26 取模的结果是 16 % 26 = 16,映射为 'j'。
因此,连接映射字符后形成的字符串是 "rij"。

示例 2:

输入: words = ["a","b","c"], weights = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
输出: "yyy"
解释:
每个单词的权重均为 1。对 26 取模的结果是 1 % 26 = 1,映射为 'y'。
因此,连接映射字符后形成的字符串是 "yyy"。

示例 3:

输入: words = ["abcd"], weights = [7,5,3,4,3,5,4,9,4,2,2,7,10,2,5,10,6,1,2,2,4,1,3,4,4,5]
输出: "g"
解释:
"abcd" 的权重是 7 + 5 + 3 + 4 = 19。对 26 取模的结果是 19 % 26 = 19,映射为 'g'。
因此,连接映射字符后形成的字符串是 "g"。

说明:

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

思路

已知小写字母的权重数组中 weights,计算每个单词的权重之和对 26 取模,将结果倒序映射回小写字母,即 0 -> z1 -> y ……

根据题意模拟即可。

代码


/**
 * @date 2026-06-15 14:51
 */
public class MapWordWeights3838 {

    public String mapWordWeights(String[] words, int[] weights) {
        StringBuilder sb = new StringBuilder();
        for (String word : words) {
            int sum = 0;
            for (int i = 0; i < word.length(); i++) {
                sum += weights[word.charAt(i) - 'a'];
            }
            char c = (char) ('z' - sum % 26);
            sb.append(c);
        }
        return sb.toString();
    }

}

性能

3093.最长公共后缀查询

目标

给你两个字符串数组 wordsContainer 和 wordsQuery 。

对于每个 wordsQuery[i] ,你需要从 wordsContainer 中找到一个与 wordsQuery[i] 有 最长公共后缀 的字符串。如果 wordsContainer 中有两个或者更多字符串有最长公共后缀,那么答案为长度 最短 的。如果有超过两个字符串有 相同 最短长度,那么答案为它们在 wordsContainer 中出现 更早 的一个。

请你返回一个整数数组 ans ,其中 ans[i]是 wordsContainer中与 wordsQuery[i] 有 最长公共后缀 字符串的下标。

示例 1:

输入:wordsContainer = ["abcd","bcd","xbcd"], wordsQuery = ["cd","bcd","xyz"]
输出:[1,1,1]
解释:
我们分别来看每一个 wordsQuery[i] :
对于 wordsQuery[0] = "cd" ,wordsContainer 中有最长公共后缀 "cd" 的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 1 的字符串,因为它的长度为 3 ,是最短的字符串。
对于 wordsQuery[1] = "bcd" ,wordsContainer 中有最长公共后缀 "bcd" 的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 1 的字符串,因为它的长度为 3 ,是最短的字符串。
对于 wordsQuery[2] = "xyz" ,wordsContainer 中没有字符串跟它有公共后缀,所以最长公共后缀为 "" ,下标为 0 ,1 和 2 的字符串都得到这一公共后缀。这些字符串中, 答案是下标为 1 的字符串,因为它的长度为 3 ,是最短的字符串。

示例 2:

输入:wordsContainer = ["abcdefgh","poiuygh","ghghgh"], wordsQuery = ["gh","acbfgh","acbfegh"]
输出:[2,0,2]
解释:
我们分别来看每一个 wordsQuery[i] :
对于 wordsQuery[0] = "gh" ,wordsContainer 中有最长公共后缀 "gh" 的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 2 的字符串,因为它的长度为 6 ,是最短的字符串。
对于 wordsQuery[1] = "acbfgh" ,只有下标为 0 的字符串有最长公共后缀 "fgh" 。所以尽管下标为 2 的字符串是最短的字符串,但答案是 0 。
对于 wordsQuery[2] = "acbfegh" ,wordsContainer 中有最长公共后缀 "gh" 的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 2 的字符串,因为它的长度为 6 ,是最短的字符串。

说明:

  • 1 <= wordsContainer.length, wordsQuery.length <= 10^4
  • 1 <= wordsContainer[i].length <= 5 * 10^3
  • 1 <= wordsQuery[i].length <= 5 * 10^3
  • wordsContainer[i] 只包含小写英文字母。
  • wordsQuery[i] 只包含小写英文字母。
  • wordsContainer[i].length 的和至多为 5 * 10^5 。
  • wordsQuery[i].length 的和至多为 5 * 10^5 。

思路

// todo

代码

性能

3121.统计特殊字母的数量II

目标

给你一个字符串 word。如果 word 中同时出现某个字母 c 的小写形式和大写形式,并且 每个 小写形式的 c 都出现在第一个大写形式的 c 之前,则称字母 c 是一个 特殊字母 。

返回 word 中 特殊字母 的数量。

示例 1:

输入:word = "aaAbcBC"
输出:3
解释:
特殊字母是 'a'、'b' 和 'c'。

示例 2:

输入:word = "abc"
输出:0
解释:
word 中不存在特殊字母。

示例 3:

输入:word = "AbBCab"
输出:0
解释:
word 中不存在特殊字母。

说明:

  • 1 <= word.length <= 2 * 10^5
  • word 仅由小写和大写英文字母组成。

思路

有一个字符串 word,返回其中大小写同时存在,且 所有小写都在其大写之前出现 的的字母个数。

3120_统计特殊字母的数量I 相比,本题多了顺序条件。

使用两个数组标记字母(无论大小写,统一用 0 ~ 25 表示)是否被计数以及是否被删除:

  • 如果当前字母是大写:
    • 之前大小写都已出现(计数 或者 删除都已经处理过了),直接跳过
    • 对应的小写已经出现,计数(上面的条件保证了不会重复计数),并标记为已计数
  • 如果当前字母是小写:
    • 之前大小写都已出现,且已计数(不一定被计数,因为小写可能后出现)未被标记为已删除,则计数减一,并标记为已删除
    • 注意如果之前只出现过大写,不用处理,因为没有被计数

代码


/**
 * @date 2026-05-26 9:06
 */
public class NumberOfSpecialChars3121 {

    /**
     * A(65):  1000001
     * Z(90):  1011010
     * a(97):  1100001
     * z(122): 1111010
     * 31: 0011111
     * 32: 0100000
     */
    public int numberOfSpecialChars_v1(String word) {
        Set<Integer> set = new HashSet<>();
        int n = word.length();
        boolean[] rm = new boolean[26];
        boolean[] add = new boolean[26];
        int res = 0;
        for (int i = 0; i < n; i++) {
            int c = word.charAt(i);
            // 将字母映射到 0 ~ 25,不区分大小写
            int index = (c & 31) - 1;
            // flag 为 0 表示大写字母,为 1 是小写字母
            int flag = c & 32;
            // 如果之前已经遇到过字母的大小写
            if (set.contains(c) && set.contains(c ^ (1 << 5))) {
                // 如果当前是大写,无需处理,因为需要加的话前面已经加了,需要减的前面已经减了
                if (flag == 0){
                    continue;
                }
                // 如果当前是小写,计数过且没减过,计数减一,并标记
                // 注意,如果之前只遇到过大写,不用处理,因为不满足条件没有被计数
                if (!rm[index] && add[index]){
                    rm[index] = true;
                    res--;
                }
            }
            // 如果当前是大写并且之前出现过小写,标记并计数
            if (flag == 0 && set.contains(c + 32)){
                add[index] = true;
                res++;
            }
            set.add(c);
        }
        return res;
    }

}

性能

3120.统计特殊字母的数量I

目标

给你一个字符串 word。如果 word 中同时存在某个字母的小写形式和大写形式,则称这个字母为 特殊字母 。

返回 word 中 特殊字母 的数量。

示例 1:

输入:word = "aaAbcBC"
输出:3
解释:
word 中的特殊字母是 'a'、'b' 和 'c'。

示例 2:

输入:word = "abc"
输出:0
解释:
word 中不存在大小写形式同时出现的字母。

示例 3:

输入:word = "abBCab"
输出:1
解释:
word 中唯一的特殊字母是 'b'。

说明:

  • 1 <= word.length <= 50
  • word 仅由小写和大写英文字母组成。

思路

有一个字符串 word,返回其中大小写同时存在的的字母个数。

使用哈希表记录已经出现的字母,如果已经同时出现过大小写,需要跳过,避免重复计数。否则,如果当前字母是小写,判断对应大写是否在哈希表中,如果当前字母是大写,判断对应小写是否在哈希表中,将当前字母加入哈希表。

代码


/**
 * @date 2026-05-26 8:51
 */
public class NumberOfSpecialChars3120 {

    public int numberOfSpecialChars(String word) {
        Set<Character> set = new HashSet<>();
        int n = word.length();
        int res = 0;
        for (int i = 0; i < n; i++) {
            char c = word.charAt(i);
            if (set.contains(c) && (set.contains((char) (c + 32)) || set.contains((char) (c - 32)))) {
                continue;
            }
            if ((c < 'a' && set.contains((char) (c + 32))) || (c >= 'a' && set.contains((char) (c - 32)))) {
                res++;
            }
            set.add(c);
        }
        return res;
    }

}

性能