codeforces 386C Diverse Substrings

https://codeforces.com/problemset/problem/386/C

题意:

对于字符串ss有多样性d(s)d(s)表示字符串里面不同字符的个数,然后k从1到d(s)d(s),问s有多少个的子串的多样性刚好等于k。

题解:

本来想找题字符串题做做的,结果这题是个披着字符串皮的技巧题。
对于某个值k来说,单独求它的子串有多少个其实不好处理,但对于多样性>=k>=k有多少个子串却很好求,可以用尺取法在O(n)O(n)时间内处理出来。记f(k)f(k)为多样性>=k>=k的子串的个数,那答案很明显就是f(k+1)f(k)f(k+1)-f(k)。怎么尺取具体看代码。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
const int mod=1e9+7;
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define rep(i,a,n) for (int i=a;i<=n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
typedef long long ll;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
ll qpow(ll a,ll b){ll ans=1;a%=mod;assert(b>=0);for(;b;b>>=1){if(b&1)ans=ans*a%mod;a=a*a%mod;}return ans;}
ll gcd(ll a,ll b){return b>0?gcd(b,a%b):a;}
int n,m,T;
char s[maxn];
ll dp[30];
int cnt[30];
int add(int c){
	int res=0;
	if(!cnt[c]) res=1;
	return res;
}
int del(int c){
	int res=0;
	if(cnt[c]-1==0) res=-1;
	return res;
}
void gao(int t){
	memset(cnt,0,sizeof cnt);
	int l=1,r=0,num=0;
	for(int i=1;i<=n;i++){
		num+=add(s[i]-'a');
		cnt[s[i]-'a']++;
		if(num==t){
			r=i;
			break;
		}
	}
	ll res=0;
	for(int i=r;i<=n;i++){
		while(l<i&&num+del(s[l]-'a')>=t){
			num+=del(s[l]-'a');
			cnt[s[l]-'a']--;
			l++;
		}
		res+=l;
		if(i==n)continue;
		num+=add(s[i+1]-'a');
		cnt[s[i+1]-'a']++;
	}
	dp[t]=res;
}
ll ans[30];
int main(){
	cin>>s+1;
	n=strlen(s+1);
	m=0;
	for(int i=1;i<=n;i++){
		m+=add(s[i]-'a');
		cnt[s[i]-'a']++;
	}
	for(int i=m;i>=1;i--){
		gao(i);
		ans[i]=dp[i]-dp[i+1];
	}
	cout<<m<<endl;
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<endl;
	}
	return 0;
}
/*
*/


Q.E.D.