a
【问题描述】
你是能看到第一题的 friends呢。
—— hja
何大爷对字符串十分有研究,于是天出题虐杀 zhx。何大爷今天为 何大爷今天为 字符串定义了新的权值计算方法。一个由小写母组成,被定义为其中出现次数最多的字符减去少。(注 被定义为其中出现次数最多的字符减去少。(注 意,在讨论出现最少的字符时候该必须至一次)何大爷给你一个字符串,何大爷想知道这的所有子中权值最是多少?
【输入格式】
第一行一个整数n,代表字符串的长度。
接下来一行n个小写字母,代表该字符串。
【输出格式】
一行个整数代表答案。
【样例输入】
10
aabbaaabab
【样例输出】
3
【数据范围与规定】
对于 30%的数据, 1≤n≤100。
对于 60%的数据, 1≤n≤1000。
对于 100%的数据, 1≤n≤106.
题目大意:
求一个字符串子串的最多出现次数 减去 最少出现次数的 最大值
思路:
1.爆搜
2.正解:前缀和进行求解
枚举右端点,使用前缀和优化。
加入我们选定一段区间l~r,并且假设出现次数最多的为x,最少的为z,用一个数组sum[]统计一个字母到当前位置出现的次数,那么出现次数最多的字母与最少字母的差值显然为:
sum[x][r]-sum[x][l-1]-(sum[z][r]-sum[z][l-1])
==> sum[x][r]-sum[z][r]-sum[x][l-1]+sum[z][l-1]
==> (sum[x][r]-sum[z][r])-(sum[x][l-1]-sum[z][l-1])
然后你可以发现:前面括号中的跟后面括号中的本质上是一个东西的,长的一毛一样,只是后面的区间端点不同而已。
但是又因为l-1一定是比r要小的,所以l-1一定会在枚举r之前就已经被处理出来;
所以现在我们需要知道的就只有x和z到底是谁就ok:
我们枚举每一个点,然后再枚举另一个字母,处理出最大的sum[x][r]-sum[z][r](r是我们要枚举的位置,x是这个位置上的字母,y是枚举的另一字母)。
用来更新最大值,这样的话我们枚举出来的字母就会出现两种情况,要么x比z多,要么z比x多。
所以我们取一个最大值来更新答案,又因为这里的sum[x][l-1]-sum[y][l-1]是前面预处理出来的,所以每次在枚举到的位置取一最小值储存一下,并且记录是在什么时候更新的这个最小值。
并且还要注意的是:
当last[j]==(pos[j][now] || pos[now][j])时,如果减去的话就会直接把j这个字母消去,所以在后半段我们需要把这个多减去的1加上。
后面一部分是加没错了,可是对于前面来说就是减去1,因为(sum[x][r]-sum[z][r])-(sum[x][l-1]-sum[z][l-1])嘛!
上代码:
#include <iostream> #include <cstring> #include <cstdio> #define INF 0x7fffffff using namespace std; const int Ms = 1011; const int Ys = 26; int n,ans,p,maxx,minn=INF; char sr[Ms]; int s[Ys]; int max(int a,int b) { return a > b ? a : b; } int dfs(int op,int ed,int now) { if(ed==n) return ans; s[now]++; maxx=0,minn=INF; for(int i=0; i<Ys; ++i) { if(s[i]==0) continue; if(s[i]>maxx) maxx=s[i]; if(s[i]<minn) minn=s[i]; } if(maxx-minn>ans) ans=maxx-minn; ed++; dfs(op,ed,sr[ed]-97); } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d",&n); for(int i=0; i<n; ++i) cin>>sr[i]; for(int i=0; i<n; ++i) { p=max(p,dfs(i,i,sr[i]-97)); memset(s,0,sizeof(s)); maxx=0,minn=INF,ans=0; } printf("%d",p); return 0; }