题解:学而思编程 幸运密码
【题目来源】
学而思编程:幸运密码
【题目描述】
小明用一个密码锁来保护他的秘密,密码锁的密码是 \(3\) 位数字。
小明还有一个 \(n\) 位幸运数字串 \(S\),他打算从其中选三个数码作为他的密码。小明从这个 \(n\) 位串中删除 \(n−3\) 位数,然后把剩下的 \(3\) 个数码按照原来的顺序排列,就得到了一个 \(3\) 位密码。
问小明一共可以得到多少种不同的密码?
【输入】
第 \(1\) 行,\(1\) 个正整数 \(n\)。
第 \(2\) 行,\(1\) 个字符串 \(S\)。
【输出】
输出小明可以得到的不同密码的个数。
【输入样例】
4
0224
【输出样例】
3
【核心思想】
-
问题分析:给定一个长度为 \(n\) 的数字串 \(S\),需要从中选出 \(3\) 个数码(保持原顺序)组成一个 \(3\) 位密码,求能得到多少种不同的密码。这是一个子序列枚举 + 顺序验证问题,关键在于利用密码值域有限(仅 \(10^3 = 1000\) 种)的特性,通过枚举所有可能的密码并在原串中验证其是否为子序列。
-
算法选择:
- 值域枚举:枚举所有 \(000\) 到 \(999\) 的三位数字组合(共 \(1000\) 种)
- 子序列验证:对每个候选密码 \((a, b, c)\),在 \(S\) 中按顺序查找三个字符是否存在
-
关键步骤:
- 初始化:读取 \(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++(该密码合法)
- 遍历 \(c\) 从
- 遍历 \(b\) 从
- 遍历 \(a\) 从
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(1000 \times n)\),枚举 \(1000\) 种密码,每次子序列验证 \(O(n)\)
- 空间复杂度:\(O(n)\),存储字符串 \(S\)
-
值域枚举的核心思想:
- 值域有限性:密码为 \(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
浙公网安备 33010602011771号