2025年3月GESP真题及题解(C++七级): 等价消除

2025年3月GESP真题及题解(C++七级): 等价消除

题目描述

小 A 有一个仅包含小写英文字母的字符串 S S S。

对于一个字符串,如果能通过每次删去其中两个相同字符的方式,将这个字符串变为空串,那么称这个字符串是可以被等价消除的。

小 A 想知道 S S S 有多少子串是可以被等价消除的。

一个字符串 S ′ S' S′ 是 S S S 的子串,当且仅当删去 S S S 的某个可以为空的前缀和某个可以为空的后缀之后,可以得到 S ′ S' S′。

输入格式

第一行,一个正整数 ∣ S ∣ |S| ∣S∣,表示字符串 S S S 的长度。

第二行,一个仅包含小写英文字母的字符串 S S S。

输出格式

一行,一个整数,表示答案。

输入输出样例 1
输入 1
复制代码
7
aaaaabb
输出 1
复制代码
9
输入输出样例 2
输入 2
复制代码
9
babacabab
输出 2
复制代码
2
说明/提示

对于 20 % 20\% 20% 的测试点,保证 S S S 中仅包含 a a a 和 b b b 两种字符。

对于另外 20 % 20\% 20% 的测试点,保证 1 ≤ ∣ S ∣ ≤ 2000 1 \leq |S| \leq 2000 1≤∣S∣≤2000。

对于所有测试点,保证 1 ≤ ∣ S ∣ ≤ 2 × 10 5 1 \leq |S| \leq 2 \times 10^5 1≤∣S∣≤2×105。

题目分析

核心思路

题目要求找出字符串 S S S 中所有可以被"等价消除"的子串。根据定义,一个字符串能被等价消除,当且仅当其中每个字符的出现次数都是偶数

因此,问题转化为:找出 S S S 中所有子串,使得每个字符的出现次数都是偶数。

关键观察
  1. 奇偶性状态压缩:因为只有小写字母(26种),可以用一个 26 位的二进制数表示每个字符出现次数的奇偶性(0表示偶数次,1表示奇数次)。

  2. 前缀异或思想

    • 定义 prefix[i] 表示前 i 个字符的奇偶性状态
    • 子串 [l, r] 的状态 = prefix[r] ^ prefix[l-1]
    • 子串满足条件 ⇔ prefix[r] ^ prefix[l-1] = 0prefix[r] = prefix[l-1]
  3. 转化为计数问题 :对于每个位置 r,统计有多少个 l (0 ≤ l < r) 使得 prefix[l] = prefix[r]

  4. 高效实现:使用哈希表记录每个状态出现的次数。

算法步骤
  1. 初始化状态为 0,表示空字符串的奇偶性(所有字符出现0次,都是偶数)
  2. 遍历字符串的每个字符:
    • 更新当前奇偶性状态(异或对应字符的位)
    • 当前状态的出现次数 = 该状态之前出现的次数
    • 这些次数就是新增的满足条件的子串数量
    • 更新该状态的计数
  3. 累加所有满足条件的子串数量
时间复杂度
  • O(n),其中 n 是字符串长度
  • 使用哈希表实现状态计数的 O(1) 访问
空间复杂度
  • O(min(n, 2²⁶)),最多存储 n+1 个不同的状态

代码实现

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int n;
    string s;
    cin >> n >> s;
    
    // 状态压缩:用int的低26位表示26个字母的奇偶性
    int state = 0;  // 当前前缀的奇偶性状态
    
    // 哈希表:记录每个状态出现的次数
    unordered_map<int, long long> cnt;
    cnt[0] = 1;  // 空前缀的状态出现1次
    
    long long ans = 0;  // 结果可能很大,用long long
    
    // 遍历字符串
    for (char c : s) {
        // 更新状态:异或对应字符的位
        int bit = c - 'a';
        state ^= (1 << bit);
        
        // 当前状态之前出现的次数 = 新增的满足条件的子串数
        ans += cnt[state];
        
        // 更新该状态的计数
        cnt[state]++;
    }
    
    cout << ans << endl;
    return 0;
}

功能分析

核心功能
  1. 状态表示:使用整数的二进制位表示26个字母的奇偶性
  2. 前缀计算:通过异或运算高效更新状态
  3. 计数统计:使用哈希表记录状态出现次数
  4. 子串计数:通过状态匹配计算满足条件的子串数
正确性证明
  1. 必要性:如果子串能被等价消除,则每个字符出现偶数次,所以子串状态为0
  2. 充分性:如果子串状态为0,可以通过配对删除所有字符
  3. 计数正确prefix[r] = prefix[l] 等价于子串 [l+1, r] 状态为0
示例分析

示例1:

复制代码
输入:7
      aaaaabb
处理过程:
位置0: state=0, cnt[0]=1, ans+=1 → ans=1, cnt[0]=2
位置1: state=1(a), cnt[1]=0, ans+=0 → ans=1, cnt[1]=1
位置2: state=0, cnt[0]=2, ans+=2 → ans=3, cnt[0]=3
位置3: state=1, cnt[1]=1, ans+=1 → ans=4, cnt[1]=2
位置4: state=0, cnt[0]=3, ans+=3 → ans=7, cnt[0]=4
位置5: state=3(a,b), cnt[3]=0, ans+=0 → ans=7, cnt[3]=1
位置6: state=1, cnt[1]=2, ans+=2 → ans=9, cnt[1]=3
输出:9

示例2:

复制代码
输入:9
      babacabab
处理过程类似,最终输出2

各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

1、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html

2、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

3、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html

4、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
Yupureki1 小时前
《算法竞赛从入门到国奖》算法基础:入门篇-贪心算法(上)
c语言·数据结构·c++·算法·贪心算法·visual studio
散峰而望2 小时前
【算法竞赛】队列和 queue
开发语言·数据结构·c++·算法·链表·github·线性回归
yuanmenghao2 小时前
车载Linux 系统问题定位方法论与实战系列 - 开篇: 为什么需要一套“系统化”的 Linux 问题定位方法
linux·运维·服务器·数据结构·c++·自动驾驶
一只小bit2 小时前
Qt 对话框全方面详解,包含示例与解析
前端·c++·qt·cpp·页面
柏木乃一2 小时前
基础IO(上)
linux·服务器·c语言·c++·shell
e***98572 小时前
C++跨平台开发的5大核心挑战与突破
开发语言·c++
橘颂TA2 小时前
【剑斩OFFER】算法的暴力美学——leetCode 662 题:二叉树最大宽度
c++·算法·结构与算法
MSTcheng.2 小时前
【C++】开放定址法实现哈希表!
c++·缓存·stl·散列表·哈希
玖釉-2 小时前
[Vulkan 学习之路] 20 - 顶点缓冲区:创建顶点缓冲区 (Vertex Buffer Creation)
c++·windows·图形渲染