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;
}

浙公网安备 33010602011771号