题解:学而思编程 幸运密码

【题目来源】

学而思编程:幸运密码

【题目描述】

小明用一个密码锁来保护他的秘密,密码锁的密码是 \(3\) 位数字。

小明还有一个 \(n\) 位幸运数字串 \(S\),他打算从其中选三个数码作为他的密码。小明从这个 \(n\) 位串中删除 \(n−3\) 位数,然后把剩下的 \(3\) 个数码按照原来的顺序排列,就得到了一个 \(3\) 位密码。

问小明一共可以得到多少种不同的密码?

【输入】

第 \(1\) 行,\(1\) 个正整数 \(n\)

第 \(2\) 行,\(1\) 个字符串 \(S\)

【输出】

输出小明可以得到的不同密码的个数。

【输入样例】

4
0224

【输出样例】

3

【核心思想】

  1. 问题分析:给定一个长度为 \(n\) 的数字串 \(S\),需要从中选出 \(3\) 个数码(保持原顺序)组成一个 \(3\) 位密码,求能得到多少种不同的密码。这是一个子序列枚举 + 顺序验证问题,关键在于利用密码值域有限(仅 \(10^3 = 1000\) 种)的特性,通过枚举所有可能的密码并在原串中验证其是否为子序列。

  2. 算法选择

    • 值域枚举:枚举所有 \(000\)\(999\) 的三位数字组合(共 \(1000\) 种)
    • 子序列验证:对每个候选密码 \((a, b, c)\),在 \(S\) 中按顺序查找三个字符是否存在
  3. 关键步骤

    • 初始化:读取 \(n\)(字符串长度)和 \(S\)(数字串)
    • 枚举所有密码
      • 遍历 \(a\)'0''9'(密码第一位)
        • 遍历 \(b\)'0''9'(密码第二位)
          • 遍历 \(c\)'0''9'(密码第三位)
            • x = S.find(a):在 \(S\) 中查找第一个字符 \(a\) 的位置
            • y = S.find(b, x + 1):从 \(a\) 的下一个位置开始查找 \(b\)
            • z = S.find(c, y + 1):从 \(b\) 的下一个位置开始查找 \(c\)
            • \(x \neq -1\)\(y \neq -1\)\(z \neq -1\)ans++(该密码合法)
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(1000 \times n)\),枚举 \(1000\) 种密码,每次子序列验证 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储字符串 \(S\)
  5. 值域枚举的核心思想

    • 值域有限性:密码为 \(3\) 位数字,总共只有 \(10^3 = 1000\) 种可能,远小于从 \(n\) 位串中选 \(3\) 位的组合数 \(C_n^3\)
    • 子序列判定:利用 find 函数按顺序查找三个字符,天然保证"保持原顺序"的约束,无需显式维护下标
    • 贪心查找策略:每次 find 从上一个字符的下一个位置开始搜索,确保三个字符在原串中严格递增出现
    • 去重自动处理:枚举的是密码值而非选取位置,相同密码值无论有多少种选取方式只计一次,天然实现去重
    • 适用于"从长串中按顺序选取固定长度子序列,求不同子序列种类数"的问题,当子序列值域较小时,枚举值域比枚举位置更高效

【算法标签】

模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// ================= 全局变量 =================

int n;               // 字符串长度
int ans;             // 记录满足条件的三位数密码个数
string s;            // 给定的数字字符串

// ================= 主函数 =================
int main()
{
    // 读取字符串长度和字符串内容
    cin >> n >> s;

    // 暴力枚举所有可能的三位数字密码 (a, b, c)
    // 每一位都可以取 '0' ~ '9'
    for (char a = '0'; a <= '9'; a++)
    {
        for (char b = '0'; b <= '9'; b++)
        {
            for (char c = '0'; c <= '9'; c++)
            {
                // 在字符串 s 中依次查找 a, b, c
                // 要求 a 出现在 b 之前,b 出现在 c 之前
                int x = s.find(a);              // 查找第一个字符 a 的位置
                int y = s.find(b, x + 1);       // 从 a 的下一个位置开始查找 b
                int z = s.find(c, y + 1);       // 从 b 的下一个位置开始查找 c

                // 如果任何一个字符没有找到,跳过当前密码
                if (x == -1 || y == -1 || z == -1)
                    continue;

                // 三个字符都能按顺序找到,说明该密码是合法的
                ans++;
            }
        }
    }

    // 输出满足条件的三位数密码总数
    cout << ans << endl;

    return 0;
}

【运行结果】

4
0224
3
posted @ 2026-07-04 13:16  团爸讲算法  阅读(3)  评论(0)    收藏  举报