2025.11.21 力扣每日一题

1930.长度为3的不同回文子序列

这个题目还可以,主要是对首尾相同字符的搜索,以及两相同字符间不同字符的数目的处理。

用的力扣官方解法,简单易懂

cpp 复制代码
class Solution {
public:
    int countPalindromicSubsequence(string s) {
        int n = s.size(); // 存储字符串s的长度
        int res = 0;      // 存储最终的会问子序列数量
        // 枚举所有可能的首尾字符
        for (char ch = 'a'; ch <= 'z'; ++ch) { // 从a-z,++表示变成下一个字母
            int l = 0, r = n - 1; // 定义字符串起始索引left 末尾索引right
            // 寻找该字符第一次出现的下标
            while (l < n && s[l] != ch) {
                l++;
            }
            // 寻找该字符最后一次出现的下标
            while (r >= 0 && s[r] != ch) {
                --r;
            }
            // 如果 r - l < 2,意味着 l 和 r
            // 指向同一个位置(r-l=0),或者它们是相邻的(r-l=1),都没有足够的空间容纳一个中间字符,因此无法构成长度为
            // 3 的回文子序列 。
            if (r - l < 2) {
                // 该字符未出现,或两下标中间的子串不存在
                continue;
            }
            // 定义一个 unordered_set(无序哈希集合),用于存储在 l 和 r
            // 之间发现的所有不同字符。unordered_set 的特性是自动去重
            //  利用哈希集合统计s[l+1..r-1]子串的字符总数,并更新答案
            unordered_set<char> charset;
            // 循环遍历 l 和 r 之间的所有字符(不包括 l 和 r 本身)。
            for (int k = l + 1; k < r; ++k) {
                // 当前遍历到的字符 s[k] 插入到哈希集合 charset 中。如果 s[k]
                // 已经存在于集合中,insert 操作不会做任何事情。
                charset.insert(s[k]);
            }
            // 环结束后,charset 中存储的就是 l 和 r
            // 之间所有不重复的中间字符。集合的大小(charset.size())就是以当前
            // ch 为首尾的不同回文子序列的数量。将这个数量加到最终结果 res 上。
            res += charset.size();
        }
        return res;
    }
};

代码的核心思路是**利用回文子序列 "首尾字符相同、中间字符任意" 的特点,通过枚举首尾字符 + 统计中间字符种类来计算不同回文子序列的数量,**逐部分解释:

一、题目核心要求

需要统计长度为 3 的不同回文子序列 的个数。长度为 3 的回文子序列的结构是 aba(首尾字符相同,中间字符任意),且相同的子序列只计数一次。

二、代码逻辑拆解

1. 枚举所有可能的 "首尾字符"

长度为 3 的回文子序列,首尾字符必须相同(记为 ch)。代码中通过 for (char ch = 'a'; ch <= 'z'; ++ch) 枚举 26 个小写字母作为首尾字符。

2. 找到首尾字符的 "第一次" 和 "最后一次" 出现位置
  • l:字符 ch 在字符串 s 中第一次出现的下标 (通过 while (l < n && s[l] != ch) 从左向右找);
  • r:字符 ch 在字符串 s 中最后一次出现的下标 (通过 while (r >= 0 && s[r] != ch) 从右向左找)。

若 r - l < 2(即 l 和 r 之间没有至少 1 个字符),说明无法构成 "首尾为 ch、长度为 3" 的子序列,直接跳过当前字符。

3. 统计 "首尾之间" 的所有不同字符(中间字符)

首尾字符确定为 ch 后,中间字符可以是 s[l+1..r-1] 中的任意字符,且每个不同的中间字符对应一个不同的回文子序列 (如首尾是 a,中间是 b 对应 aba,中间是 c 对应 aca)。

代码中用 unordered_set<char> charset 统计 s[l+1..r-1] 中的所有不同字符(set 自动去重),charset.size() 就是当前首尾字符 ch 对应的不同回文子序列数量,将其累加到结果 res 中。

三、示例验证(以示例 1 s = "aabca" 为例)

  1. 枚举 ch = 'a':
    • l = 0(第一个 a),r = 4(最后一个 a);
    • 中间字符是 s[1..3] = "abc",charset 包含 a、b、c,大小为 3;
    • res += 3(此时 res = 3)。
  2. 枚举其他字符(如 b):
    • l = 2(第一个 b),r = 2(最后一个 b),r - l < 2,跳过。
  3. 最终 res = 3,与示例 1 的输出一致。

四、总结

这个代码的核心是 利用 "首尾字符相同 + 中间字符去重"的策略,通过枚举 26 个字母的首尾位置,结合哈希集合统计中间字符的种类,高效计算出所有不同的长度为 3 的回文子序列,时间复杂度为 O (26*n)(n 是字符串长度)。

相关推荐
文丑颜不良啊1 天前
LeetCode1941~1960题目解析
java·leetcode
MUSHANGXS1 天前
大模型时代的新蓝海-基于OS底层的AI-Agent应用
人工智能·职场和发展·交互
老师我太想进步了20261 天前
【数据结构-4】算法美学之顺序表
数据结构·算法
风影同志783241 天前
编码器是激活函数吗?—— 一个看起来很像、其实不是的类比
算法
UIU1141 天前
P1481 魔族密码
c++·算法·动态规划
文丑颜不良啊1 天前
LeetCode1961~1980题目解析
java·leetcode
文丑颜不良啊1 天前
LeetCode2001~2020题目解析
java·leetcode
加宝的起点雷达1 天前
40 条候选里 38 条来自同一个源,主卡整屏重样
人工智能·算法·创业
秋天的怀念_David1 天前
算法一千零一夜 · 第二十六夜 协议的母语
java·spring boot·算法