| 序号 | 问题 | 常用模板/思路 | 蓝桥杯真题 (链接) |
|---|---|---|---|
| 1 | 统计不同字符个数 | 使用set集合存储字符,返回len(set)即可 |
不同子串 (2019 省赛) 子串分值 (2020 省赛) |
| 2 | 判断是否有重复字符 | 利用set去重后比较长度,或用collections.Counter统计频次 |
重复字符串 (2020 国赛) 重复的串 (2024 国赛) |
| 3 | 找出第一个只出现一次的字符 | 1. 用哈希表 (Counter) 统计频率 2. 再次遍历字符串,找到第一个频率为1的字符 |
字串分值 (2020 省赛) 找第一个只出现一次的字符 |
| 4 | 统计每个字符出现次数 | 使用collections.Counter直接生成频数字典 |
重复字符串 (2020 国赛) 子串分值和 (2020 省赛) |
| 5 | 字符串去重 | 使用set进行去重(会丢失顺序) 或遍历字符串,维护一个seen集合和一个结果列表 |
子串去重 (2025 国赛) 串中取3个不重复字母 |
| 6 | 判断两个字符串字母相同 | 对两个字符串的字符进行排序 (sorted) 后比较是否相等 |
字符串对比 (基础练习) |
| 7 | 最长无重复字符子串 | 滑动窗口 : 维护窗口,用集合/数组记录字符是否出现过,右指针扩展,遇重复则移动左指针 | 最长不重复子串 (省赛训练题) (此题为LeetCode经典题的蓝桥版) |
| 8 | 拆分字符串使两部分不同字符之和最大 | 根据题目不同,常用前缀和思想 (预计算每个分割点前后的不同字符数)或暴力枚举分割点 | 切开字符串 (2015 国赛) 分割字符串 (2024 国赛 |
一个字符串的非空子串是指字符串中长度至少为
1
的连续的一段字符组成的串。例如,字符串
𝑎
𝑎
𝑎
𝑏
有非空子串
𝑎
,
𝑏
,
𝑎
𝑎
,
𝑎
𝑏
,
𝑎
𝑎
𝑎
,
𝑎
𝑎
𝑏
,
𝑎
𝑎
𝑎
𝑏
,一共
7
个。注意在计算时,只算本质不同的串的个数。
请问,字符串
0100110001010001
有多少个不同的非空子串?
cpp
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 请在此输入您的代码
set<string> a;
string s="0100110001010001";
for(int len=1;len<=s.size();len++){
for(int i=0;i<=s.size()-len;i++){
a.insert(s.substr(i,len));
}
}
cout<<a.size();
return 0;
}
子串分值和
题目描述
对于一个字符串
𝑆
,我们定义
𝑆
的分值
𝑓
(
𝑆
)
为
𝑆
中出现的不同的字符个数。例如
𝑓
(
"
𝑎
𝑏
𝑎
"
)
=
2
,
𝑓
(
"
𝑎
𝑏
𝑐
"
)
=
3
,
𝑓
(
"
𝑎
𝑎
𝑎
"
)
=
1
。
现在给定一个字符串
𝑆
0... 𝑛 − 1
(长度为
𝑛
),请你计算对于所有
𝑆
的非空子串
𝑆
𝑖 . . . 𝑗
(
0
≤
𝑖
≤
𝑗
<
𝑛
)
,
𝑓
(
𝑆
𝑖 . . . 𝑗
)
的和是多少。
输入描述
输入一行包含一个由小写字母组成的字符串
𝑆
。
其中,
1
≤
𝑛
≤
1
0
5
。
输出描述
输出一个整数表示答案。
输入输出样例
示例 1
输入
ababc

输出
28
只能过60%样例,
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int n = s.size();
long long ans = 0;
for (int i = 0; i < n; ++i) {
unordered_set<char> seen; // 或者 set<char>
for (int j = i; j < n; ++j) {
seen.insert(s[j]);
ans += seen.size();
}
}
cout << ans << endl;
return 0;
}
定义 dp[i] 表示以第 i 个字符结尾的所有子串的分值之和。
转移:dp[i] = dp[i-1] + (i - last[s[i]]),其中 last[c] 是字符 c 上一次出现的位置(初始为 -1)。
最后答案 = sum(dp[i])。
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
string s; cin >> s;
int n = s.size();
vector<int> last(26, -1);
long long ans = 0, dp = 0;
for (int i = 0; i < n; ++i) {
int c = s[i] - 'a';
dp += i - last[c];
ans += dp;
last[c] = i;
}
cout << ans << endl;
return 0;
}
三、
重复字符串
题目描述
如果一个字符串
𝑆
恰好可以由某个字符串重复
𝐾
次得到,我们就称
𝑆
是
𝐾
次重复字符串。例如 abcabcabc可以看作是 abc 重复
3
次得到,所以 abcabcabc 是
3
次重复字符串。
同理 aaaaaa 既是
2
次重复字符串、又是
3
次重复字符串和
6
次重复字符串。
现在给定一个字符串
𝑆
,请你计算最少要修改其中几个字符,可以使
𝑆
变为一个
𝐾
次字符串?
输入描述
输入第一行包含一个整数
𝐾
。
第二行包含一个只含小写字母的字符串
𝑆
。
其中,
1
≤
𝐾
≤
1
0
5
,
1
≤
∣
𝑆
∣
≤
1
0
5
。其中
∣
𝑆
∣
表示
𝑆
的 长度。
输出描述
输出一个整数代表答案。如果
𝑆
无法修改成
𝐾
次重复字符串,输出
−
1
。
输入输出样例
示例 1
输入
2
aabbaa

输出
2
对于这个问题,如果字符串长度 n 不是 K 的倍数,直接输出 -1。否则,令 m = n / K,将字符串分成 K 段,每段长度为 m。要使所有段完全相同,对于每个偏移位置 i(0 ≤ i < m),需要将第 0 段、第 1 段、......、第 K-1 段的第 i 个字符改成相同字符。统计这 K 个字符中出现次数最多的字符,保留它,其余 K - max_cnt 个字符需要修改。对所有 i 求和即得最少修改次数。
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
int K;
string S;
cin >> K >> S;
int n = S.size();
if (n % K != 0) {
cout << -1 << endl;
return 0;
}
int m = n / K;
int ans = 0;
for (int i = 0; i < m; ++i) {
int cnt[26] = {0};
for (int j = 0; j < K; ++j) {
char c = S[j * m + i];
cnt[c - 'a']++;
}
int maxcnt = *max_element(cnt, cnt + 26);
ans += K - maxcnt;
}
cout << ans << endl;
return 0;
}