#P2803. 第1题-小红改试卷
-
1000ms
Tried: 62
Accepted: 22
Difficulty: 3
所属公司 :
阿里
时间 :2025年4月8日-阿里淘天(算法岗)
-
算法标签>动态规划
第1题-小红改试卷
题解
题面描述
试卷由n道单项选择题构成,每道题只有a, b, c, d四个选项。小红在批改试卷时,如果在答案序列中存在一个子序列恰好等于字符串"abc",她会产生一定的愤怒值。其中,子序列是指从原字符串中删除任意个(可以为零、也可以为全部)字符后保留剩余字符的顺序。如果答案序列中存在多个不同的方式可以得到"abc"这个子序列,那么小红的愤怒值就是所有方式的数量。
思路
我们可以利用动态规划(或累加统计)的思想,从左至右遍历字符串,同时维护以下三个变量:
- count_a:记录遍历过程中出现的字符a的个数。
- count_ab:记录能够构成子序列"ab"的种数。对于每个新出现的b,它可以与之前的所有a组合,所以更新方式为:count_ab+=count_a。
- count_abc:记录子序列"abc"的种数。每当遍历到一个c时,它可以和之前的"ab"组合,因此更新方式为:count_abc+=count_ab。
对于字符 d或其他字符,不会影响计数,因此忽略它们。
该方法时间复杂度为O(n),空间复杂度为O(1)。
代码分析
-
变量定义:
使用count_a, count_ab, count_abc三个变量分别统计a、"ab"、"abc"的出现方式。 -
状态转移:
- 若当前字符为a,则count_a自增。
- 若当前字符为b,则所有之前的a均可构成"ab"组合,即count_ab+=count_a。
- 若当前字符为c,则所有之前的"ab"均可构成"abc"组合,即count_abc+=count_ab。
-
输出结果:
最终答案为count_abc。
C++ 解法
#include <iostream>
#include <string>
using namespace std;
int main(){
// 输入字符串答案序列, 字符串长度为n
string s;
cin >> s;
// 定义变量, 分别统计字符a, 子序列"ab", 子序列"abc"
long long count_a = 0, count_ab = 0, count_abc = 0;
// 遍历整个字符串
for(char ch : s){
if(ch == 'a'){
// 对于字符a, 自增计数
count_a++;
}
else if(ch == 'b'){
// 对于字符b, 可以与之前所有a组合得到"ab"子序列
count_ab += count_a;
}
else if(ch == 'c'){
// 对于字符c, 可以与之前所有"ab"组合得到"abc"子序列
count_abc += count_ab;
}
// 字符d不会影响子序列的统计, 故忽略
}
// 输出子序列"abc"的统计值, 即小红的愤怒值
cout << count_abc << endl;
return 0;
}
Python
# 读入答案序列, 字符串长度为n
s = input().strip()
# 初始化变量, 用于分别统计a, 子序列"ab", 子序列"abc"
count_a = 0 # 统计字符a出现次数
count_ab = 0 # 统计子序列"ab"出现的种数
count_abc = 0 # 统计子序列"abc"出现的种数
# 遍历字符串中每个字符
for ch in s:
if ch == 'a':
# 遇到a, 更新count_a自增1
count_a += 1
elif ch == 'b':
# 遇到b, 可与之前所有的a组合得到"ab"子序列
count_ab += count_a
elif ch == 'c':
# 遇到c, 可与之前所有的"ab"组合得到"abc"子序列
count_abc += count_ab
#d字符不影响计数, 所以直接跳过
# 输出结果, 即子序列"abc"的总种数
print(count_abc)
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// 创建 Scanner 读取输入, 输入字符串为只包含a, b, c, d的序列
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine().trim();
scanner.close();
// 初始化用于分别统计a, 子序列"ab", 子序列"abc"的变量
long countA = 0; // 记录a的累计个数
long countAB = 0; // 记录子序列"ab"的累计种数
long countABC = 0; // 记录子序列"abc"的累计种数
// 遍历整个字符串
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch == 'a') {
// 当字符为a, 增加countA
countA++;
} else if (ch == 'b') {
// 当字符为b, 所有之前的a均可与该b组合成"ab"子序列, 累加至countAB
countAB += countA;
} else if (ch == 'c') {
// 当字符为c, 所有之前的"ab"均可与该c组合成"abc"子序列, 累加至countABC
countABC += countAB;
}
// $d$ 字符不影响子序列统计, 因此直接忽略
}
// 输出结果, 即子序列"abc"的总数
System.out.println(countABC);
}
}
题目内容
小红正在批改同学的试卷,试卷由n道单项选择题构成,每道题只有a,b,c,d 四个选项.
如果在答案序列中存在恰好为"abc"的子序列,那么小红会认为只是随手写的答案,会产生一点愤怒值. 给你这个答案序列,你需要求出小红会产生多少愤怒值
子序列为从原字符串中删除任意个(可以为零、可以为全部)字符得到的新字符串。
输入描述
第一行输出一个长度为n的只存在a,b,c,d字符串序列,表示答案序列(1≤n≤105)
输出描述
输出一个整数,表示结果。
样例1
输入
abcc
输出
2