力扣每日一题

909. 蛇梯棋 - 力扣(LeetCode)

timer:2025-5-31

class Solution {
public:
    int snakesAndLadders(vector<vector<int>>& board) {
        int n = board.size();
        vector<int8_t> vis(n * n + 1); // 记录每个位置是否被访问过
        vis[1] = true; // 起点标记为已访问
        vector<int> q; // BFS队列
        q.push_back(1); // 从位置1开始
        
        // BFS主循环,i表示当前步数
        for(int i = 0; !q.empty(); i++){
            auto t = q; // 保存当前层的所有位置
            q.clear();  // 清空队列准备下一层
            
            // 遍历当前层的所有位置
            for(auto& x : t){
                if(x == n * n){ // 到达终点
                    return i;
                }
                
                // 模拟掷骰子,枚举所有可能的下一步位置
                for(int y = x + 1; y <= min(x + 6, n * n); y++){
                    // 将一维位置y转换为二维棋盘坐标
                    int r = (y - 1) / n, c = (y - 1) % n;
                    if(r % 2) // 奇数行需要镜像列坐标(注意这里的奇偶行指的是数组的奇偶行,也就是从最上方开始为第0行)
                        c = n - c - 1;
                        
                    // 获取棋盘上该位置的值
                    int nex = board[n - 1 - r][c];
                    if(nex == -1) // 没有梯子或蛇
                        nex = y;
                        
                    // 如果下一步位置未被访问过
                    if(!vis[nex]){
                        q.push_back(nex); // 加入队列
                        vis[nex] = true;  // 标记为已访问   
                    }
                }
            }
        }
        return -1; // 无法到达终点
    }
};

2025-6: 

2929. 给小朋友们分糖果 II - 力扣(LeetCode)

timer:2025-6-1

class Solution {
    // 计算组合数C(n,2) = n*(n-1)/2
    long long C2(long long n) {
        return n > 1 ? n * (n - 1) / 2 : 0;
    }
public:
    /*
     * 计算将n个无区别糖果放入3个有区别盒子的合法分配方案数
     * 每个盒子最多放limit个糖果
     * 
     * 容斥原理应用:
     * 总方案数(允许空盒) = C(n+2,2) (隔板法)
     * 设Ai表示第i个盒子超过limit的方案集合
     * 需要计算 |A1∪A2∪A3| 并从总方案中扣除
     * 
     * 根据容斥公式:
     * |A1∪A2∪A3| = Σ|Ai| - Σ|Ai∩Aj| + |A1∩A2∩A3|
     * 
     * 单集合大小:|Ai| = C(n - limit, 2) (共3个)
     * 双集合交集:|Ai∩Aj| = C(n - 2*limit - 1, 2) (共3个)
     * 三集合交集:|A1∩A2∩A3| = C(n - 3*limit - 2, 2) (若存在)
     */
    long long distributeCandies(int n, int limit) {
        return C2(n + 2)                     // 总方案数
             - 3 * C2(n - limit + 1)          // 减去单集合
             + 3 * C2(n - 2 * limit)          // 加上双集合交集
             - C2(n - 3 * limit - 1);         // 减去三集合交集
    }
};

 135. 分发糖果 - 力扣(LeetCode)

timer:2025-6-2

class Solution {
public:
    int candy(vector<int>& ratings) {
        int n = ratings.size();
        if (n == 0) return 0;
        
        // 初始化每个孩子至少有一个糖果
        vector<int> candies(n, 1);
        
        // 第一次遍历:从左到右,确保右边评分高的孩子比左边的多
        for (int i = 1; i < n; i++) {
            if (ratings[i] > ratings[i-1]) {
                candies[i] = candies[i-1] + 1;
            }
        }
        
        // 第二次遍历:从右到左,确保左边评分高的孩子比右边的多
        for (int i = n-2; i >= 0; i--) {
            if (ratings[i] > ratings[i+1]) {
                candies[i] = max(candies[i], candies[i+1] + 1);
            }
        }
        
        // 计算总糖果数
        int total = 0;
        for (int num : candies) {
            total += num;
        }
        
        return total;
    }
};

 1298. 你能从盒子里获得的最大糖果数 - 力扣(LeetCode)

timer:2025-6-3

// DFS
// 记录状态
// 1、找到钥匙(状态0也表示为有钥匙)
// 2、找到箱子
// 3、当钥匙和箱子都找到我们就能拿到糖果
class Solution {
public:
    int maxCandies(vector<int>& status, vector<int>& candies, vector<vector<int>>& keys, vector<vector<int>>& containedBoxes, vector<int>& initialBoxes) {
        auto& has_key = status;
        vector<uint8_t> has_box(status.size());

        // 标记已知箱
        for(auto& x : initialBoxes)
            has_box[x] = true;
        
        int ans = 0;
        // 开箱函数
        auto dfs = [&](this auto&& dfs, int x) -> void {
            ans += candies[x]; // 拿糖
            has_box[x] = false;

            // 拿到钥匙
            for(int y : keys[x]){
                has_key[y] = true;
                if(has_box[y]) // 有钥匙对应的箱子
                    dfs(y);
            }
            // 拿到箱子
            for(int y : containedBoxes[x]){
                has_box[y] = true;
                if(has_key[y]) // 有箱子对应的钥匙
                    dfs(y);
            }
        };

        // 开始开箱
        for(auto& x : initialBoxes)
            if(has_box[x] && has_key[x])
                dfs(x);

        return ans;
    }
}; 

 3403. 从盒子中找出字典序最大的字符串 I - 力扣(LeetCode)

timer: 2025-6-4

一:枚举子串左端点

// 枚举子串左端点解法
class Solution {
public:
    string answerString(string word, int numFriends) {
        int n = word.size();
        string ans = "";
        // 特殊情况:当每个朋友恰好分到1个字符时,整个字符串即为答案
        if(numFriends == 1)
            return word;
        // 枚举所有可能的子串左端点i
        for(int i = 0; i < n; i++){
            // 计算从i开始的有效子串长度(受numFriends限制)
            // 子串长度至少为1,且不能超过原字符串长度
            int len = n - max((numFriends - 1), i);
            if(len <= 0) continue; // 跳过无效长度
            // 更新最大字典序子串
            ans = max(ans, word.substr(i, len));
        }
        return ans;
    }
};

二: 计算字典序最大的后缀

// 双指针优化解法:线性时间复杂度
class Solution {
public:
    string answerString(string word, int numFriends) {
        // 特殊情况处理
        if (numFriends == 1) {
            return word;
        }
        int n = word.size();
        int i = 0; // 当前最大字典序子串的起始位置
        int j = 1; // 探索指针,尝试寻找更大的子串
        while (j < n) {
            int len = 0; // 公共前缀长度
            // 计算i和j开始的子串的最长公共前缀
            while (j + len < n && word[i + len] == word[j + len]) {
                len++;
            }
            // 如果j位置子串在公共前缀后更大,则更新i
            if (j + len < n && word[i + len] < word[j + len]) {
                int t = i; // 保存旧的i值
                i = j;     // 更新i为新的更大子串起始位置
                // 优化j的下一个位置:跳过已知不可能的起始点
                j = max(j + 1, t + len + 1);
            } else {
                // 否则直接跳过公共前缀长度+1的距离
                j += len + 1;
            }
        }
        // 计算最终子串长度(受numFriends限制)
        // 确保子串长度至少为1,且不超过原字符串长度
        int len = n - max(numFriends - 1, i);
        return word.substr(i, len);
    }
};

1061. 按字典序排列最小的等效字符串 - 力扣(LeetCode)

timer:2025-6-5

class Solution {
public:
    string smallestEquivalentString(string s1, string s2, string baseStr) {
        // 初始化并查集数组:parent[x] 表示字符 x 的父节点,初始时每个节点的父节点是自身
        int parent[26];
        for (int i = 0; i < 26; ++i) {
            parent[i] = i;
        }

        // 查找字符 x 所在集合的根节点(带路径压缩)
        function<int(int)> findRoot = [&](int x) {
            if (parent[x] != x) {
                parent[x] = findRoot(parent[x]); // 路径压缩:直接将节点连接到根节点
            }
            return parent[x];
        };

        // 合并两个字符所在的集合,确保每个集合的根节点是字典序最小的字符
        auto unionChars = [&](char c1, char c2) {
            int root1 = findRoot(c1 - 'a');
            int root2 = findRoot(c2 - 'a');
            
            if (root1 != root2) {
                // 让较大的根节点指向较小的根节点,确保每个集合的根是最小字符
                if (root1 > root2) {
                    parent[root1] = root2;
                } else {
                    parent[root2] = root1;
                }
            }
        };

        // 处理所有等价关系,建立字符间的连通性
        for (int i = 0; i < s1.size(); ++i) {
            unionChars(s1[i], s2[i]);
        }

        // 将 baseStr 中的每个字符替换为其等价类中的最小字符
        for (char& c : baseStr) {
            c = findRoot(c - 'a') + 'a';
        }

        return baseStr;
    }
};

 2434. 使用机器人打印字典序最小的字符串 - 力扣(LeetCode)

timer:2025-6-6

class Solution {
public:
    string robotWithString(string s) {
        int n = s.size();
        
        // 预处理后缀最小值数组
        // suf_min[i] 表示从索引i到字符串末尾的最小字符
        vector<char> suf_min(n + 1);
        suf_min[n] = 'z'; // 初始化边界值为最大字符'z'
        for (int i = n - 1; i >= 0; i--) {
            suf_min[i] = min(suf_min[i + 1], s[i]);
        }

        string ans;     // 存储最终结果的字符串
        stack<char> st; // 模拟机器人的栈操作
        
        // 遍历输入字符串的每个字符
        for (int i = 0; i < n; i++) {
            st.push(s[i]); // 将当前字符压入栈
            
            // 核心逻辑:如果栈顶字符小于等于剩余字符中的最小字符
            // 则应该立即将其添加到结果中,确保字典序最小
            while (!st.empty() && st.top() <= suf_min[i + 1]) {
                ans += st.top();
                st.pop();
            }
        }
        
        return ans;
    }
};

 3423. 循环数组中相邻元素的最大差值 - 力扣(LeetCode)

timer: 2025-6-12

class Solution {
public:
    int maxAdjacentDistance(vector<int>& nums) {
        int n = nums.size();
        int ans = abs(nums[n - 1] - nums[0]);

        for(int i = 1; i < n; i++){
            ans = max(abs(nums[i] - nums[i - 1]), ans);
        }

        return ans; 
    }
};

 2566. 替换一个数字后的最大差值 - 力扣(LeetCode)

timer: 2025-6-14

// 简单贪心
// 最大值: 将最前面的数字替换为最大‘9’
// 最小值: 将最前面的数字替换为最小‘0’
// 注: 要注意全部替换
class Solution {
public:
    int minMaxDifference(int num) {
        string s = to_string(num);

        // 替换成最大值
        int mmax = num;
        for (char ch : s) {
            if (ch != '9') {
                string tmp = s;
                ranges::replace(tmp, ch, '9');
                mmax = stoi(tmp);
                break;
            }
        }

        // 替换成最小值
        int mmin;
        char ch0 = s[0];
        ranges::replace(s, ch0, '0');
        mmin = stoi(s);

        
        return mmax - mmin;
    }
};

 2016. 增量元素之间的最大差值 - 力扣(LeetCode)

timer: 2025-6-16

// 方法一:暴力枚举法
// 时间复杂度:O(n²),空间复杂度:O(1)
class Solution {
public:
    int maximumDifference(vector<int>& nums) {
        int n = nums.size();  // 获取数组长度
        int ans = -1;         // 初始化最大差值为-1(题目要求未找到正差值时返回-1)

        // 外层循环遍历每个可能的起始元素
        for(int i = 0; i < n; i++)
            // 内层循环遍历每个可能的结束元素(需在起始元素之后)
            for(int j = i + 1; j < n; j++)
                // 若当前元素对满足正差值条件,则更新最大差值
                if(nums[j] - nums[i] > 0)
                    ans = max(ans, nums[j] - nums[i]);
                
        return ans;  // 返回最大正差值,若不存在则返回-1
    }
};

// 方法二:一次遍历优化法
// 时间复杂度:O(n),空间复杂度:O(1)
class Solution {
public:
    int maximumDifference(vector<int>& nums) {
        int n = nums.size();  // 获取数组长度
        int ans = 0;          // 初始化最大差值为0
        int mmin = INT_MAX;   // 初始化最小值为INT_MAX,用于跟踪已遍历元素中的最小值

        // 一次遍历数组
        for(int i = 0; i < n; i++){
            // 计算当前元素与历史最小值的差值,并更新最大差值
            ans = max(ans, nums[i] - mmin);
            // 更新已遍历元素中的最小值
            mmin = min(nums[i], mmin);
        }
        
        // 若最大差值为0,说明不存在正差值,返回-1;否则返回最大差值
        return ans ? ans : -1;
    }
};

 1432. 改变一个整数能得到的最大差值 - 力扣(LeetCode)

timer: 2025-6-16

class Solution {
public:
    int maxDiff(int num) {
        // 将数字转换为字符串以便逐位处理
        string s = to_string(num);

        // 定义一个lambda函数用于替换字符串中的指定字符并转换回整数
        // oldChar: 需要替换的字符
        // newChar: 替换后的新字符
        auto replace_num = [&](char oldChar, char newChar) {
            int x = 0;
            for (auto& ch : s) {
                // 如果当前字符等于oldChar,则替换为newChar,否则保持不变
                char c = ch == oldChar ? newChar : ch;
                // 逐位构建新的整数值
                x = x * 10 + (c - '0');
            }
            return x;
        };

        // 计算替换后能得到的最大数值
        int mmax = num;
        // 遍历字符串,找到第一个不是'9'的字符
        for (char ch : s) {
            if (ch != '9') {
                // 将该字符替换为'9'以获得可能的最大值
                mmax = replace_num(ch, '9');
                break;
            }
        }

        // 计算替换后能得到的最小数值
        int mmin = num;
        // 如果第一位不是'1',则将第一位替换为'1'
        if (s[0] != '1') {
            mmin = replace_num(s[0], '1');
        } else {
            // 否则从第二位开始寻找第一个大于'1'的字符
            for (int i = 1; i < s.size(); i++) {
                if (s[i] > '1') {
                    // 将该字符替换为'0'以获得可能的最小值
                    mmin = replace_num(s[i], '0');
                    break;
                }
            }
        }

        // 返回最大值和最小值的差值
        return mmax - mmin;
    }
};

3405. 统计恰好有 K 个相等相邻元素的数组数目 - 力扣(LeetCode)

timer: 2025-6-17

const int MOD = 1'000'000'007;
const int MX = 100'000;
typedef long long ll;

ll F[MX]; // 阶乘
ll INV_F[MX]; // 对应阶乘的逆元

// 快速幂
ll qpow(ll x, int n){
    ll res = 1;
    for(; n; n /= 2){
        if(n % 2){
            res = res * x % MOD;
        }
        x = x * x % MOD;
    }
    return res;
}

// 初始化阶乘
auto init = []{
    F[0] = 1;
    for(int i = 1; i < MX; i++)
        F[i] = F[i - 1] * i % MOD;
    
    INV_F[MX - 1] = qpow(F[MX - 1], MOD - 2);
    for(int i = MX - 1; i > 0; i--){
        INV_F[i - 1] = INV_F[i] * i % MOD;
    }
    return 0;
}();

// 计算组合
ll comb(int n, int m){
    return F[n] * INV_F[m] % MOD * INV_F[n - m] % MOD;
}

class Solution {
public:
    int countGoodArrays(int n, int m, int k) {
        return comb(n - 1, k) * m % MOD * qpow(m - 1, n - k - 1) % MOD;
    }
};

// 作者:灵茶山艾府
// 链接:https://leetcode.cn/problems/count-the-number-of-arrays-with-k-matching-adjacent-elements/solutions/3033292/chun-shu-xue-ti-pythonjavacgo-by-endless-mxj7/
// 来源:力扣(LeetCode)
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2966. 划分数组并满足最大差限制 - 力扣(LeetCode)

timer:2025-6-18

class Solution {
public:
    vector<vector<int>> divideArray(vector<int>& nums, int k) {
        int n = nums.size();
        
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        
        // 遍历每个分组,每组3个元素
        for (int i = 0; i < n; i += 3) {
            // 检查当前分组的最大值和最小值之差是否不超过k
            if (nums[i+2] - nums[i] > k) {
                return {};
            }
            // 将当前分组加入结果集
            ans.push_back({nums[i], nums[i+1], nums[i+2]});
        }
        
        return ans;
    }
};

2294. 划分数组使最大差为 K - 力扣(LeetCode)

timer:2025-6-19

class Solution {
public:
    int partitionArray(vector<int>& nums, int k) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        int pre = nums[0], ans = 1; // 要注意先排序在初始化pre
        
        // if (n == 0) return 0;
        for(int i = 1; i < n; i++){
            if((nums[i] - pre) > k){
                pre = nums[i];
                ans++;
            }
        }

        return ans;
    }
};

3443. K 次修改后的最大曼哈顿距离 - 力扣(LeetCode)

timer: 2025-6-20

/*
 * 方法一:曼哈顿距离最大化算法
 * 
 * 坐标简化:
 * - 向左(l) 向右(r) 向上(u) 向下(d)
 * 
 * 曼哈顿距离公式:
 * distance = |l - r| + |u - d|
 * 
 * 优化目标:
 * 通过修改方向字符,最大化 |l - r| 和 |u - d|
 * 
 * 优化策略:
 * 1. 每次修改可以将一个方向字符变为其相反方向(例如:E→W 或 N→S)
 * 2. 每次修改可以使对应方向的计数变化量+2
 * 3. 最优修改次数为 min(l, r, k) 或 min(u, d, k)
 * 
 * 最终距离计算公式:
 * distance = |l - r| + 2 * move
 * distance = |u - d| + 2 * move
 * 其中 move = min(同向计数, 反向计数, 剩余修改次数k)
 */
class Solution {
public:
    int maxDistance(string s, int k) {
        // 统计四个方向的总计数
        int l = 0, r = 0, u = 0, d = 0;
        int ans = 0;
        
        // 遍历字符串逐步更新方向计数并计算最大可能距离
        for (char ch : s) {
            // 更新方向计数
            if (ch == 'N')      u++;
            else if (ch == 'S') d++;
            else if (ch == 'E') r++;
            else                l++;
            
            // 剩余可用修改次数
            int x = k;
            
            // 计算单个轴向的最大可能距离
            auto func = [&](int a, int b) -> int {
                // 计算可转换的最大次数:受限于同向计数、反向计数和剩余修改次数
                int move = min({a, b, x});
                // 扣除已使用的修改次数
                x -= move;
                // 计算转换后的轴向距离:原始距离 + 2 * 转换次数
                return abs(a - b) + 2 * move;
            };
            
            // 更新最大曼哈顿距离:x轴向距离 + y轴向距离
            ans = max(ans, func(l, r) + func(u, d));
        }
        
        return ans;
    }
};

// 方法二
class Solution {
public:
    int maxDistance(string s, int k) {
        int ans = 0;        // 记录最大可能的曼哈顿距离
        int x = 0, y = 0;   // 当前位置的坐标,初始化为原点(0,0)
        
        // 遍历字符串中的每个字符
        for (int i = 0; i < s.size(); i++) {
            // 根据字符更新当前位置
            if (s[i] == 'N') y++;       // 北:y坐标增加
            else if (s[i] == 'S') y--;  // 南:y坐标减少
            else if (s[i] == 'E') x++;  // 东:x坐标增加
            else x--;                   // 西:x坐标减少
            
            // 计算当前可能的最大曼哈顿距离
            // abs(x) + abs(y):当前位置的曼哈顿距离
            // k * 2:使用k次修改机会可能增加的最大距离(每次修改增加2个单位)
            // i + 1:当前已处理的字符数,即最多可以修改的次数
            // min(...):确保修改次数不超过可用次数
            // max(ans, ...):更新最大距离
            ans = max(ans, min(abs(x) + abs(y) + k * 2, i + 1));
        }
        
        return ans;  // 返回最大可能的曼哈顿距离
    }
};

3085. 成为 K 特殊字符串需要删除的最少字符数 - 力扣(LeetCode)

timer: 2025-6-21

// 方法一: 贪心枚举最小频率基准值
// 时间复杂度: O(n + 26²) ≈ O(n)
// 空间复杂度: O(26) ≈ O(1)
class Solution {
public:
    int minimumDeletions(string word, int k) {
        // 统计每个字符的出现次数
        vector<int> count(26, 0);
        for (char ch : word) {
            count[ch - 'a']++;
        }
        // 按频率升序排序,便于贪心枚举基准值
        sort(count.begin(), count.end());

        int maxSave = 0; // 最多可保留的字符数

        // 枚举所有可能的最小频率基准值 base = count[i]
        // 即假设保留的字符中最少出现次数为 base
        for (int i = 0; i < 26; i++) {
            int base = count[i];
            int sum = 0;

            // 遍历所有频率 ≥ base 的字符(频率 < base 的字符已被自动排除)
            for (int j = i; j < 26; j++) {
                // 对于频率 ≥ base 的字符,保留其原始频率或 base + k 中的较小值
                // 确保保留的字符频率不超过 base + k
                sum += min(count[j], base + k);
            }

            // 更新最大保留字符数
            maxSave = max(maxSave, sum);
        }

        // 总删除次数 = 原字符串长度 - 最多可保留的字符数
        return word.size() - maxSave;
    }
};
// 方法二: 滑动窗口优化
// 时间复杂度: O(n + 26 log 26) ≈ O(n)
// 空间复杂度: O(26) ≈ O(1)
class Solution {
public:
    int minimumDeletions(string word, int k) {
        // 统计每个字符的出现次数
        vector<int> count(26, 0);
        for (char ch : word) {
            count[ch - 'a']++;
        }
        // 按频率升序排序,便于贪心枚举
        sort(count.begin(), count.end());

        int maxSave = 0;      // 最多可保留的字符数
        int windowSum = 0;    // 当前窗口内字符的总出现次数
        int right = 0;        // 滑动窗口的右边界

        // 枚举所有可能的最小频率值 base(贪心策略)
        for (int base : count) {
            // 扩展窗口右边界,直到窗口内所有字符的频率 ≤ base + k
            // 这些字符可以完整保留(频率在 [base, base+k] 范围内)
            while (right < 26 && count[right] <= base + k) {
                windowSum += count[right];
                right++;
            }

            // 计算当前方案的总保留字符数:
            // 1. 窗口内的字符按原始频率保留(windowSum)
            // 2. 窗口外的字符按最大可能保留(每个字符保留 base + k 个)
            int currentSave = windowSum + (base + k) * (26 - right);
            maxSave = max(maxSave, currentSave);

            // 窗口左边界右移,移除当前基准值 base
            // 为下一轮更大的 base 做准备
            windowSum -= base;
        }

        // 总删除次数 = 原字符串长度 - 最多可保留的字符数
        return word.size() - maxSave;
    }
};

 2138. 将字符串拆分为若干长度为 k 的组 - 力扣(LeetCode)

timer:2025-6-22

// class Solution {
// public:
//     vector<string> divideString(string s, int k, char fill) {
//         vector<string> ans;
//         int l = 0, r = k - 1;
//         while(s.size() % k){
//             s += fill;
//         }
//         int n = s.size();
//         int count = n / k;
//         while(count--){
//             string path = "";
//             for(int i = l; i <= r; i++){
//                 path += s[i];
//             }
//             ans.push_back(path);
//             l += k;
//             r += k;
//         }

//         return ans;
//     }
// };
class Solution {
public:
    vector<string> divideString(string s, int k, char fill) {
        vector<string> result;
        int n = s.size();
        
        // 计算需要填充的字符数
        int padding = (k - (n % k)) % k;
        // 一次性填充所有需要的字符
        s.append(padding, fill);
        
        // 遍历字符串,每次处理k个字符
        for (int i = 0; i < s.size(); i += k) {
            // 直接使用子字符串,避免逐个字符拼接
            result.push_back(s.substr(i, k));
        }
        
        return result;
    }
};

 2081. k 镜像数字的和 - 力扣(LeetCode)

timer: 2025-6-23

// 存储各进制(2-9)下的k镜像数,ans[k]表示k进制下的k镜像数列表
vector<long long> ans[10];
// 每个进制需要收集的k镜像数的最大数量
const int mmax = 30;

/**
 * 判断数字x在k进制下是否为回文数
 * @param x 待判断的十进制数
 * @param k 目标进制
 * @return true表示x在k进制下是回文数,false表示不是
 */
bool isKthBasePalindrome(long long x, int k) {
    // 排除k进制下有前导零的情况(若x能被k整除且x不为0,则k进制表示有前导零)
    if (x % k == 0 && x != 0) {
        return false;
    }

    // 反转k进制数字
    long long rev = 0;
    // 优化循环条件,减少不必要的计算
    while (rev < x / k) {
        rev = rev * k + x % k;
        x /= k;
    }

    // 比较反转前后的数字,考虑数字长度为奇数的情况
    return rev == x || rev == x / k;
}

/**
 * 检查是否已为每个进制收集到足够数量的k镜像数
 * @param x 待检查的十进制回文数
 * @return true表示所有进制都已收集满mmax个k镜像数,false表示未收集满
 */
bool checkCountN(long long x) {
    bool flag = true;
    // 遍历2到9的所有进制
    for (int k = 2; k <= 9; k++) {
        // 如果当前进制收集的k镜像数未满且x在该进制下是回文数,则添加到列表
        if (ans[k].size() < mmax && isKthBasePalindrome(x, k)) {
            ans[k].push_back(x);
        }

        // 只要有一个进制未收集满,就继续收集
        if (ans[k].size() < mmax) {
            flag = false;
        }
    }
    if (!flag) {
        return false;
    }

    // 计算每个进制下k镜像数的前缀和(用于快速获取前n项和)
    for (int k = 2; k <= 9; k++) {
        partial_sum(ans[k].begin(), ans[k].end(), ans[k].begin());
    }
    return true;
}

// 全局初始化lambda表达式,程序启动时自动执行
auto init = []() {
    // 按位数生成十进制回文数,base表示当前处理的位数的基数(1, 10, 100, ...)
    for (int base = 1;; base *= 10) {
        // 生成奇数位十进制回文数
        for (int i = base; i < base * 10; i++) {
            long long x = i;
            // 通过反转前半部分生成奇数位回文数(如i=123,生成12321)
            for (int j = i / 10; j > 0; j /= 10) {
                x = x * 10 + j % 10;
            }
            // 检查该回文数是否为各进制的k镜像数
            if (checkCountN(x)) {
                return 0; // 所有进制收集满后退出初始化
            }
        }
        // 生成偶数位十进制回文数
        for (int i = base; i < base * 10; i++) {
            long long x = i;
            // 通过反转前半部分生成偶数位回文数(如i=1234,生成1234321)
            for (int j = i; j > 0; j /= 10) {
                x = x * 10 + j % 10;
            }
            // 检查该回文数是否为各进制的k镜像数
            if (checkCountN(x)) {
                return 0; // 所有进制收集满后退出初始化
            }
        }
    }
}();

class Solution {
public:
    /**
     * 获取k进制下的第n个k镜像数
     * @param k 目标进制(2-9)
     * @param n 第n个k镜像数
     * @return k进制下的第n个k镜像数(1-based索引)
     */
    long long kMirror(int k, int n) { 
        return ans[k][n - 1]; // 转换为0-based索引获取结果
    }
};

 2200. 找出数组中的所有 K 近邻下标 - 力扣(LeetCode)

timer: 2025-6-24

class Solution {
public:
    vector<int> findKDistantIndices(vector<int>& nums, int key, int k) {
        vector<int> ans; // 存储结果的数组
        
        // 初始化last为-k-1,确保当数组前k个元素中没有key时,last < i-k
        // 这样可以正确处理数组起始部分的边界情况
        int last = -k - 1;
        
        // 初始化阶段:从k-1到0逆序查找第一个key的位置
        // 目的是为数组的第一个窗口(索引0到k)设置正确的last值
        for(int i = k - 1; i >= 0; i--){
            if(nums[i] == key){
                last = i; // 找到key后更新last为该索引
                break;    // 只需要最近的key,找到后立即退出循环
            }
        }

        // 主循环:遍历数组中的每个元素
        for(int i = 0; i < nums.size(); i++){
            // 检查当前位置i的右侧k位置是否为key
            // 如果是,则更新last为该位置,因为这是当前能找到的最近的key
            if(i + k < nums.size() && nums[i + k] == key)
                last = i + k;
                
            // 判断条件:如果last >= i - k,表示在i的左侧或当前位置存在key
            // 且该key与i的距离不超过k,因此i符合条件
            if(last >= i - k)
                ans.push_back(i);
        }

        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值