P11965 [GESP202503 七级] 等价消除

题目

传送门

思路

借鉴了洛谷的第二篇题解

什么是可以被等价消除的子串?

其中每个字符的出现次数必须全为偶数

如何判断?

我们用一个 26 位的二进制数来记录当前各字符出现次数的奇偶性。每一位对应一个小写字母:若某位为 1,表示该字母出现了奇数次;若为 0,则表示偶数次。
若两个前缀 S[1…u] 和 S[1…v] 的状态相同,则子串 S[u+1…v] 满足所有字符出现偶数次。因为此时两前缀的奇偶性差为 0,说明中间子串的每个字符变化了偶数次。
所以我们可以用数组 s 记录各状态的出现次数。遍历字符串时,每遇到一个状态 x,就将s[x]累加到结果中(因为此前所有相同状态的位置均可作为子串起点),然后将s[x]++。
最终结果即为所有合法子串的数量。

代码

有注释

#include<bits/stdc++.h>
using namespace std;
const int N=6.8e7+5; 
int s[N];
long long ans=0;
string a; 
int main(){
	std::ios::sync_with_stdio(false);//关闭输入输出流 
	std::cin.tie(0);
	std::cout.tie(0);
	int n;
	cin>>n;
	cin>>a;
	a=" "+a;//使下标从1开始 
	s[0]++;//`s[0]++,就是手动存入空前缀(下标 0)这个初始状态,允许后面的位置和起点配对,算出从第一个字符开始的合法子串。 
	int sum=0;
	for(int i=1;i<=n;i++){
		int x=a[i]-'a';
		sum^=1<<x;//异或 
		ans+=s[sum];//加上前面的起点 
		s[sum]++;//自己也要加上 
	}
	cout<<ans;
	return 0;
} 

无注释

#include<bits/stdc++.h>
using namespace std;
const int N=6.8e7+5; 
int s[N];
long long ans=0;
string a; 
int main(){
	std::ios::sync_with_stdio(false); 
	std::cin.tie(0);
	std::cout.tie(0);
	int n;
	cin>>n;
	cin>>a;
	a=" "+a;
	s[0]++;
	int sum=0;
	for(int i=1;i<=n;i++){
		int x=a[i]-'a';
		sum^=1<<x;
		ans+=s[sum];
		s[sum]++;
	}
	cout<<ans;
	return 0;
} 

完结撒花 QWQ

posted @ 2026-08-31 12:28  陈晨2011  阅读(6)  评论(0)    收藏  举报