题目描述
题目链接:所有子字符串美丽值之和
一个字符串的 美丽值 定义为:出现频率最高字符与出现频率最低字符的出现次数之差。
比方说,“abaacc” 的美丽值为 3 - 1 = 2 。
给你一个字符串 s ,请你返回它所有子字符串的 美丽值 之和。
示例1
输入:s = “aabcb”
输出:5
解释:美丽值不为零的字符串包括 [“aab”,“aabc”,“aabcb”,“abcb”,“bcb”] ,每一个字符串的美丽值都为 1 。
示例2
输入:s = “aabcbaa”
输出:17
提示:
- 1 <= s.length <= 500
- s 只包含小写英文字母。
思路分析
前缀和:
要想快速得到某个区间内某个字母出现的次数,使用前缀和可以在O(1)的时间内得到结果
由于有26个英文字母,所以我们开26个一维数组,每个一维数组存放对应字母的前缀和
以aabcb举例,首先是a,所以cnt[0][1]++,其余的cnt不变
同样s[2] = a,所以,cnt[0][2]++,其余不变
以此类推,最后得到的结果是
例如要求s[2~4] = abc
那么其中含有a的个数为cnt[0][4] - cnt[0][1] = 1
那么其中含有b的个数为cnt[1][4] - cnt[1][1] = 1
那么其中含有c的个数为cnt[2][4] - cnt[2][1] = 1
所以可以在常数时间内求出每一段子字符串中每个字母的个数
代码
int cnt[26][510];
class Solution {
public:
int beautySum(string s) {
//一定要初始化,即使cnt放在class以外,其内部的值也是未经过初始化的
memset(cnt, 0, sizeof cnt);
//要计算前缀和,所以在s前加一个空格,不用进行下标转换
s = ' ' + s;
//计算前缀和数组
for(int i = 1; i < s.size(); i++) {
for(int j = 0; j < 26; j++) {
cnt[j][i] += cnt[j][i - 1];
}
cnt[s[i] - 'a'][i]++;
}
int res = 0;
//长度从3开始计算,长度为1和2都没有意义(不信你举个例子)
for(int k = 3; k < s.size(); k++) {
for(int i = 1; i + k - 1 < s.size(); i++) {
int maxn = -1, minn = 1e9;
for(int j = 0; j < 26; j++) {
int t = cnt[j][i + k - 1] - cnt[j][i - 1];
//如果这个区间内没有该字母,跳过!
if(!t) continue;
if(t > maxn) maxn = t;
if(t < minn) minn = t;
}
res += maxn - minn;
}
}
return res;
}
};