C++消消乐 2023年CSP-S认证真题 CCF信息学奥赛C++ 中小学提高组 第二轮真题解析

#「开学季干货」:聚焦知识梳理与经验分享#

C++消消乐

博主推荐

1、C++专栏 

  1. 电子学会C++一级历年真题解析
  2. 电子学会C++二级历年真题解析
  3. 蓝桥杯C++选拔赛真题解析
  4. 信息素养大赛C++算法编程挑战赛

 2、Python专栏

  1. 蓝桥杯python选拔赛真题详解
  2. 蓝桥杯python省赛真题详解
  3. 蓝桥杯python国赛真题详解
  4. 信息素养大赛python编程挑战赛
  5. python等级一级真题解析【电子学会】
  6. python等级二级真题解析【电子学会】
  7. python等级三级真题解析【电子学会】

一、题目要求

1、编程实现

小 L 现在在玩一个低配版本的消消乐,该版本的游戏是一维的,一次也只能消除两个相邻的元素。

现在,他有一个长度为 n 且仅由小写字母构成的字符串。我们称一个字符串是可消除的,当且仅当可以对这个字符串进行若干次操作,使之成为一个空字符串。

其中每次操作可以从字符串中删除两个相邻的相同字符,操作后剩余字符串会拼接在一起。

小 L 想知道,这个字符串的所有非空连续子串中,有多少个是可消除的。

2、输入输出

输入描述:

输入的第一行包含一个正整数 n,表示字符串的长度。

输入的第二行包含一个长度为 n 且仅由小写字母构成的的字符串,表示题目中询问的字符串。

输出描述:输出一行包含一个整数,表示题目询问的答案。

输入样例:

8
accabccb

输出样例:

5

【样例解释】

一共有 5 个可消除的连续子串,分别是 ccaccaccbccbaccabccb

【数据范围】

对于所有测试数据有:1≤n≤2×106,且询问的字符串仅由小写字母构成。

测试点n≤特殊性质
1∼510
6∼7800
8∼108000
11∼122×10^5A
13∼142×10^5B
15∼172×10^5
18∼202×10^6

特殊性质 A:字符串中的每个字符独立等概率地从字符集中选择。

特殊性质 B:字符串仅由 a 和 b 构成。

二、算法分析

  1. 从给定题目的初步分析可以看出,本题要求的是能消除的个数。
  2. 策略1(PTS50):由于是要进行两两消除,所以需要检查字符串的所有非空连续子串,判断每个子串是否可消除。如果一个子串可以通过反复删除相邻相同字符最终变为空,则它是可消除的。
  3. 可以使用一个栈来模拟起始位置对应的字母,遍历子串的字符,当栈非空且栈顶字符与当前字符相同时,弹出栈顶(表示消除);否则,将当前字符压入栈。
  4. 但是这种方法当n比较大的时候会出现超时
  5. 策略2(PTS100):仔细分析会发现可消除的子串具有递归结构。如果一个子串可消除,那么它可以被分解为更小的可消除子串的组合。所以可以使用贪心+动态规划算法进行实现。
  6. 定义dp[i]:表示在第i个位置能够消除的的子串数量,同时用一个数组pr记录当前第i个字符前一次出现的位置;如果pr[i]=j,表示s[j]和s[i]可以消除,同时s[j到i]之间也是可以消除的。
  7. 查找的时候如果当前位置 i 的字符与前一个位置 i-1 不同,则跳到 pr[i-1]-1 继续尝试匹配,直到找到对应字符或者无法匹配

三、程序编写

方法1

#include <bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin >> n;
	string s;
	cin >> s;
	while(s.length() != n)
		cin >> s;
  	long long ans = 0;
    for (int i = 0; i < n; i++) {
        vector<char> st;
        for (int j = i; j < n; j++) {
            if (!st.empty() && st.back() == s[j]) 
                st.pop_back();
            else 
                st.push_back(s[j]);
                
            if (st.empty()) 
                ans++;
        }
    }
    cout << ans << endl;
    return 0;
}


方法2

#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 6;
int dp[N], pr[N];

int main() {
	int n;
	string s;
	cin >> n >> s;
	long long res;
	for (int i = 0; i < n; ++i) {
		pr[i] = i - 1;
		while (pr[i] >= 0 && s[pr[i]] != s[i])
			pr[i] = pr[pr[i]] - 1; //匹配字符的起始位置的前一个位置
		if (pr[i] > 0)
			dp[i] = dp[pr[i] - 1] + 1;
		else dp[i] = (pr[i] == 0);
		res += dp[i];
	}
	cout << res << endl;
	return 0;
}

本文作者:小兔子编程 作者首页:小兔子编程-CSDN博客

四、运行结果

8
accabccb
5

4
aaaa
4

五、考点分析

难度级别:难,这题相对而言有一定难度,具体主要考察如下:

  1. 分析题目,找到解题思路
  2. 掌握模拟算法的原理和使用方法
  3. 学会字符串或者字符数组的使用
  4. 学会贪心算法、动态规划算法的原理及应用
  5. 能够准确分析出字符消除匹配的机智和跳转思路
  6. 学会STL 容器的灵活运用:尤其是map和vector的原理和使用
  7. 学会数学建模能力以及数学运算与逻辑处理能力
  8. 学会循环语句的熟练使用和分支语句的的控制处理(for、if)
  9. 学会调试和校对程序设计与逻辑的完整性
  10. 学会分析题目,算法分析,将复杂问题模块化,简单化,从中找到相应的解题思路
  11. 充分掌握数组定义和使用、分支语句、循环语句和动态规划算法的应用

PS:方式方法有多种,小朋友们只要能够达到题目要求即可!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小兔子编程

您的鼓励是我创作优质案例的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值