随笔 4

2017 年欧洲信息学奥林匹克竞赛 Day1 Problem A

很好的题目。

题意简述

给一个长度为 \(n\) 的字符串,字符集为 \(\Sigma\),求这个字符串含有 \(|\Sigma|\) 个不同字母的子串的数量。


好久没见到这样的好题了。

我们发现正解对我这样的蒟蒻有些困难,于是考虑随机化+哈希。

以下令 \(t=|\Sigma|\)

我们对字符集内 \(t\) 种字符赋上 \(t\) 种值,使得 \(t\) 个数的和为 \(0\),否则和不为 \(0\)

然后瞬间难度降级,我们需要统计有多少个区间映射的值的和为 \(0\)

这很套路,我们做一个前缀和,把枚举到某个位置得到的和记到桶 \(p\) 里面,最后答案为:

\[\sum \frac{p_i\times(p_i-1)}{2} \]

需要特别注意的是,这种 rand 生成的随机数值域很小,不够随机,要多乘几个降低冲突概率。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
bool cnt[300];
int tot;
char c[100];
int t[300];
map<int,int> p;
signed main(){
	int n;
	cin>>n;
	string s;
	cin>>s;
	for(int i=0;i<n;i++) {
		if(!cnt[s[i]]) {
			cnt[s[i]]=1;
			c[++tot]=s[i];
		}
	} 
	//为字母映射一个值 
	srand(time(0));
	srand(rand());
	int sum=0;
	for(int i=1;i<=tot/2;i++) {
		int x=rand()*rand()*rand(); //Attention
		t[c[i]]=-x;
		sum+=t[c[i]];
	}
	for(int i=tot/2+1;i<=tot-1;i++) {
		int y=rand()*rand()*rand();
		t[c[i]]=y;
		sum+=t[c[i]];
	}
	t[c[tot]]=(0-sum);
//	for(int i=1;i<=tot;i++) {
//		cout<<c[i]<<" ";
//		cout<<t[c[i]]<<"\n";
//	}
	p[0]=1;
	sum=0;
	for(int i=0;i<n;i++) {
		sum+=t[s[i]];
		p[sum]++;
	}
	int ans=0;
	for(auto i:p) {
		int tmp=1ll*i.second*(i.second-1ll)/2ll%mod;
		ans=(ans+tmp)%mod;
	}
	cout<<ans;
	return 0;
}
posted @ 2026-02-20 21:19  AnOIer  阅读(6)  评论(0)    收藏  举报
//雪花飘落效果