一、例题精讲
例题 1:三带一
| 项目 | 内容 |
|---|---|
| 类型 | 字符串 + 模拟 |
| 核心 | 统计字符出现次数,判断是否为AAAB型 |
题目描述
小蓝和小桥玩斗地主,小蓝只剩四张牌了,判断是否是"三带一"牌型。
三带一 :四张手牌中,有三张一样,另外一张不与其他牌相同 ,即 AAAB 型。
输入 : T T T 轮,每轮一个长度为4的字符串
输出 :每轮输出 Yes 或 No
关键思路
判断逻辑:
- 去重后只有 2种字符 (
len(set(s)) == 2) - 其中一种出现 1次或3次 (
s.count(s[0]) == 1 or s.count(s[0]) == 3)
💡 为什么这样判断? 因为四张牌只有两种字符,如果第一种出现1次,第二种必出现3次;如果第一种出现3次,第二种必出现1次。两种情况都是三带一!
推演验证
输入: 222K
set('222K') = {'2', 'K'} → len=2 ✓
'222K'.count('2') = 3 → 满足条件 ✓
输出: Yes ✓
输入: 2233
set('2233') = {'2', '3'} → len=2 ✓
'2233'.count('2') = 2 → 不满足条件 ✗
输出: No ✓
题解
python
t = int(input())
for _ in range(t):
s = input()
# 去重后只有2种字符,且其中一种出现1次或3次
if len(set(s)) == 2 and (s.count(s[0]) == 1 or s.count(s[0]) == 3):
print('Yes')
else:
print('No')
复杂度 :时间 O ( T × 4 ) O(T \times 4) O(T×4),空间 O ( 1 ) O(1) O(1)
关键细节
| 坑点 | 说明 |
|---|---|
s.count(s[0]) |
统计第一个字符出现次数,利用对称性判断 |
len(set(s))==2 |
必须恰好2种字符,1种(AAAA)或3种都不行 |
| 或运算的优先级 | and 优先级高于 or,需要加括号! |
例题 2:DNA序列修正 ⭐重点
| 项目 | 内容 |
|---|---|
| 类型 | 贪心 + 模拟 |
| 核心 | 优先交换修正两个位置,无法交换则替换 |
题目描述
两条DNA序列,需要让第二条与第一条互补(A-T, C-G)。
操作规则:
- 选择第二条DNA的任意两个位置,交换他们的字符(一次修正两个位置)
- 选择第二条DNA任意一个位置,替换为A/C/G/T中的一个(一次修正一个位置)
限制 :每个位置上的碱基只能被操作一次!
目标 :求最小操作次数。
关键思路
贪心策略:能交换就不替换!
- 交换操作:一次可以修正两个位置,代价为1
- 替换操作:一次只能修正一个位置,代价为1
所以优先尝试交换,无法交换的位置再用替换。
交换条件 :对于位置 i i i 和 j j j:
- b [ i ] b[i] b[i] 应该是 a [ i ] a[i] a[i] 的互补碱基
- b [ j ] b[j] b[j] 应该是 a [ j ] a[j] a[j] 的互补碱基
- 交换后: b [ i ] b[i] b[i] 变成 d i c [ a [ i ] ] dic[a[i]] dic[a[i]] 且 b [ j ] b[j] b[j] 变成 d i c [ a [ j ] ] dic[a[j]] dic[a[j]]
即:b[j] == dic[a[i]] 且 b[i] == dic[a[j]]
推演验证
输入:
N=5
a = ACGTG
b = ACGTC
dic = {'A':'T','T':'A','C':'G','G':'C'}
位置0: a[0]=A, dic[A]=T, b[0]=A ≠ T ❌
位置1: a[1]=C, dic[C]=G, b[1]=C ≠ G ❌
位置2: a[2]=G, dic[G]=C, b[2]=G ≠ C ❌
位置3: a[3]=T, dic[T]=A, b[3]=T ≠ A ❌
位置4: a[4]=G, dic[G]=C, b[4]=C == C ✓
遍历位置0:
找j>0满足 b[j]==dic[a[0]]=T 且 b[0]==dic[a[j]]
j=3: b[3]=T == dic[A]=T ✓, b[0]=A == dic[T]=A ✓
交换b[0]和b[3]: b变成 TCGCA
ans=1
遍历位置1:
b[1]=C ≠ dic[C]=G ❌
找j>1满足条件... 找不到
ans=2 (替换)
等等,这样得到2,但样例输出是2... ✓
实际上位置1和位置2也可以交换:
b[1]=C, b[2]=G
交换后: b[1]=G(✓), b[2]=C(✓)
ans=2 ✓
题解
python
N = int(input())
a = list(input())
b = list(input())
dic_of_DNA = {'A':'T', 'T':'A', 'C':'G', 'G':'C'}
ans = 0
for i in range(N):
if b[i] != dic_of_DNA[a[i]]: # 位置i不匹配
# 贪心:优先找可以交换的位置j
for j in range(i + 1, N):
# 交换条件:b[j]是a[i]的互补,且b[i]是a[j]的互补
if b[j] == dic_of_DNA[a[i]] and b[i] == dic_of_DNA[a[j]]:
b[i], b[j] = b[j], b[i] # 交换
break # 找到就退出
ans += 1 # 一次操作(交换或替换)
print(ans)
复杂度 :时间 O ( N 2 ) O(N^2) O(N2),空间 O ( 1 ) O(1) O(1)
关键细节
| 坑点 | 说明 |
|---|---|
| 交换条件 | b[j]==dic[a[i]] and b[i]==dic[a[j]],必须双向满足 |
| break的重要性 | 找到交换对象后立即break,避免重复操作 |
| ans统计 | 无论交换还是替换,ans都+1,因为都是一次操作 |
| 贪心正确性 | 交换一次修两个,替换一次修一个,优先交换一定最优 |
例题 3:计算函数值 ⭐数学转化
| 项目 | 内容 |
|---|---|
| 类型 | 递归 + 数学 |
| 核心 | 发现规律:S(x) = 二进制中1的个数 + 1 + 1 +1 |
题目描述
神秘函数 S ( x ) S(x) S(x) 定义:
- S ( 0 ) = 1 S(0) = 1 S(0)=1
- x x x 为偶数: S ( x ) = S ( x / 2 ) S(x) = S(x/2) S(x)=S(x/2)
- x x x 为奇数: S ( x ) = S ( x − 1 ) + 1 S(x) = S(x-1) + 1 S(x)=S(x−1)+1
求 S ( x ) S(x) S(x) 的值。
关键思路
手动推演找规律:
S(0) = 1
S(1) = S(0) + 1 = 2
S(2) = S(1) = 2
S(3) = S(2) + 1 = 3
S(4) = S(2) = 2
S(5) = S(4) + 1 = 3
S(6) = S(3) = 3
S(7) = S(6) + 1 = 4
发现规律:
| x | 二进制 | S(x) | 二进制1的个数 |
|---|---|---|---|
| 0 | 0 | 1 | 0 + 1 |
| 1 | 1 | 2 | 1 + 1 |
| 2 | 10 | 2 | 1 + 1 |
| 3 | 11 | 3 | 2 + 1 |
| 4 | 100 | 2 | 1 + 1 |
| 5 | 101 | 3 | 2 + 1 |
| 6 | 110 | 3 | 2 + 1 |
| 7 | 111 | 4 | 3 + 1 |
结论 :S(x) = 二进制表示中1的个数 + 1 + 1 +1
推演验证(样例)
输入: x=7
7 = 111,1的个数 = 3
S(7) = 3 + 1 = 4 ✓
递归验证:
S(7) = S(6) + 1
= S(3) + 1
= S(2) + 1 + 1
= S(1) + 2
= S(0) + 1 + 2
= 1 + 3 = 4 ✓
题解
解法一:直接递归(题目给定范围 x ≤ 10 6 x \leq 10^6 x≤106,递归深度最多20层)
python
x = int(input())
def sm(x):
if x == 0:
return 1
if x % 2 == 0:
return sm(x // 2)
else:
return sm(x - 1) + 1
print(sm(x))
解法二:利用二进制性质( O ( log x ) O(\log x) O(logx))
python
x = int(input())
# S(x) = 二进制1的个数 + 1
print(bin(x).count('1') + 1)
复杂度:
- 递归版:时间 O ( log x ) O(\log x) O(logx)(递归深度),空间 O ( log x ) O(\log x) O(logx)(递归栈)
- 二进制版:时间 O ( log x ) O(\log x) O(logx),空间 O ( 1 ) O(1) O(1)
关键细节
| 坑点 | 说明 |
|---|---|
| 递归终止条件 | x==0 返回1,不是0 |
| 奇偶判断 | 先判断偶数(x%2==0),因为0也是偶数 |
| 数学规律 | 发现 S ( x ) = p o p c o u n t ( x ) + 1 S(x) = popcount(x) + 1 S(x)=popcount(x)+1 可以极大简化代码 |
例题 4:穿越时空之门 ⭐进制转换
| 项目 | 内容 |
|---|---|
| 类型 | 进制转换 + 枚举 |
| 核心 | 二进制数位之和 == 四进制数位之和 |
题目描述
计算1到2024中,有多少个数满足:二进制表示中各数位之和 == 四进制表示中各数位之和。
关键思路
进制转换方法:
- 二进制 :
bin(n)返回'0b1010',去掉前缀后求各位数字和 - 四进制:不断除以4取余,直到商为0
四进制转换过程:
以 6 为例:
6 ÷ 4 = 1 ... 2
1 ÷ 4 = 0 ... 1
所以 6 的四进制 = 12,数位和 = 1 + 2 = 3
6 的二进制 = 110,数位和 = 1 + 1 + 0 = 2
不相等!
推演验证
i=1: 二进制=1(和=1), 四进制=1(和=1) → 相等 ✓
i=2: 二进制=10(和=1), 四进制=2(和=2) → 不相等
i=3: 二进制=11(和=2), 四进制=3(和=3) → 不相等
i=4: 二进制=100(和=1), 四进制=10(和=1) → 相等 ✓
i=5: 二进制=101(和=2), 四进制=11(和=2) → 相等 ✓
题解
python
def base4(s):
"""求四进制表示的数位之和"""
l = []
while s >= 4:
s, b = divmod(s, 4) # s=商, b=余数
l.append(b)
l.append(s) # 最后的商
return sum(l)
def base2(s):
"""求二进制表示的数位之和"""
# bin(5) = '0b101', split('b')[1] = '101'
return sum(list(map(int, bin(s).split('b')[1])))
ans = 0
for i in range(1, 2025):
if base2(i) == base4(i):
ans += 1
print(ans)
复杂度 :时间 O ( 2024 × log 2024 ) O(2024 \times \log 2024) O(2024×log2024),空间 O ( 1 ) O(1) O(1)
关键细节
| 坑点 | 说明 |
|---|---|
divmod(s, 4) |
同时返回商和余数,避免两次运算 |
bin(s).split('b')[1] |
去掉'0b'前缀,获取纯二进制字符串 |
| 范围 | range(1, 2025) 包含1到2024 |
| 四进制最后一步 | 循环结束后s<4,直接加入列表 |
例题 5:依依的画作
| 项目 | 内容 |
|---|---|
| 类型 | 字符串 + 模拟 |
| 核心 | 前一个的个位 != 后一个的十位 |
题目描述
N N N 个两位正整数,统计有多少对相邻数字不遵循规则(前一个的个位应该等于后一个的十位)。
关键思路
字符串索引:
- 个位:
a[i][1](字符串的第二个字符) - 十位:
a[i+1][0](字符串的第一个字符)
统计 :遍历 0 0 0 到 N − 2 N-2 N−2,统计 a[i][1] != a[i+1][0] 的次数。
推演验证
输入: 5
66 13 22 55 98
i=0: 66[1]=6, 13[0]=1 → 6≠1 ❌ 不遵循
i=1: 13[1]=3, 22[0]=2 → 3≠2 ❌ 不遵循
i=2: 22[1]=2, 55[0]=5 → 2≠5 ❌ 不遵循
i=3: 55[1]=5, 98[0]=9 → 5≠9 ❌ 不遵循
总共4对不遵循
输出: 4 ✓
题解
python
n = int(input())
a = list(map(str, input().split()))
# 统计不遵循规则的相邻对数
print(sum(1 for i in range(n - 1) if a[i][1] != a[i + 1][0]))
复杂度 :时间 O ( N ) O(N) O(N),空间 O ( 1 ) O(1) O(1)
关键细节
| 坑点 | 说明 |
|---|---|
| 字符串索引 | a[i][1]是个位,a[i+1][0]是十位 |
map(str, ...) |
输入直接当字符串处理,避免转int再转str |
| 遍历范围 | range(n-1),最后一对是a[n-2]和a[n-1] |
| 简洁写法 | sum(1 for ... if ...) 是Pythonic的计数方式 |
二、今日刷题总结
| 题号 | 题目 | 考点 | 难度 | 核心技巧 |
|---|---|---|---|---|
| 1 | 三带一 | 字符串统计 | ⭐⭐ | set()去重 + count()计数 |
| 2 | DNA序列修正 | 贪心模拟 | ⭐⭐⭐ | 优先交换,双向匹配条件 |
| 3 | 计算函数值 | 递归数学 | ⭐⭐⭐ | 发现规律: p o p c o u n t ( x ) + 1 popcount(x) + 1 popcount(x)+1 |
| 4 | 穿越时空之门 | 进制转换 | ⭐⭐⭐ | divmod() + bin() |
| 5 | 依依的画作 | 字符串索引 | ⭐⭐ | 字符串索引 + 生成器表达式 |
模拟题解题模板
python
# 模板1:字符统计类
cnt = {}
for c in s:
cnt[c] = cnt.get(c, 0) + 1
# 或直接用 collections.Counter
# 模板2:贪心交换类
for i in range(n):
if not match(i):
for j in range(i + 1, n):
if can_swap(i, j):
swap(i, j)
break
ans += 1
# 模板3:进制转换类
def to_base(n, base):
"""将n转换为base进制,返回数位列表"""
digits = []
while n >= base:
n, r = divmod(n, base)
digits.append(r)
digits.append(n)
return digits[::-1] # 反转得到正序
# 模板4:字符串索引类
# 两位数的个位和十位
units = s[1] # 个位
tens = s[0] # 十位
三、结语
模拟题是蓝桥杯的送分题 ,但也是丢分重灾区。今天的5道题告诉我们:
🌟 今日收获:
- 字符串处理要熟练:
set()、count()、索引访问- 贪心思想:能交换就不替换,能一次解决就不分两次
- 数学观察:递归函数先手动算几项,找规律
- 进制转换:
divmod()和bin()是利器- Pythonic写法:
sum(1 for ... if ...)简洁高效
模拟题没有固定的算法模板,但有固定的解题套路:读题→找规律→写代码→验证。多练多总结,国赛模拟题全AC不是梦!💪
如果本文对你有帮助,欢迎 点赞 👍 + 收藏 ⭐ + 关注 🔔,你的支持是我持续更新的动力!