代码源:563、字串分值和

143 阅读3分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路 logo.png

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

这是3月19日代码源div2的每日一题。

知识点:哈希表

子串分值和 - 题目 - Daimayuan Online Judge

对于一个字符串 S ,我们定义 f(S) 为 S 中出现的不同的字符个数。 例如 f(aba)=2,f(abc)=3,f(aaa)=1。

现在给定一个字符串 S (假设长度为 len),请你计算 len−1∑i=0 len−1∑ j=i f(S[i:j]) 。

输入格式

输入一行包含一个由小写字母组成的字符串 SS 。

输出格式

输出一个整数表示答案。

样例输入

ababc

样例输出

28

数据规模

所有数据保证字符串长度 len≤1000000,字符串下标从 0 到 len−1。

问题分析

第一眼写法是枚举区间长度然后把每个区间的f[s]算出来并加一起,但这做法是n^2显然超时,所以我们就换个思路。

我们可以计算每个字符的贡献,并计算他们的贡献和。怎么计算贡献呢?因为区间内重复的字符只算一个,所以对于样例ababc中第一个a字母来说,它能被计数的情况就是从上一个字母a开始(因为没有所以实际上是从它开始),区间长度从1到len的len种情况(虽然到第3个位置也有个字符a,但我们只记录一个,所以记录的还是最先出现的a),那么第一个字母a所能提供的贡献一共就是len,即5。我们再看第二个字母a,和前面说的一样,他能被计数的情况,就是从上一个相同的字母开始,枚举区间长度直到到达字符串末尾,所以第二个字母所能提供的贡献就是:(当前字母位置 - 上一个相同字母)*(区间长度 - 当前字母位置)=2 *3=6;我们只要如法炮制,求得所有字符的贡献,并把计算贡献的总和即可。

虽然看似要找上一个相同字母的位置有点麻烦,但实际上我们用一个哈希表或者26长度的数组就可以,专门记录上一个相同字符出现的位置,因为一开始还没有相同的字符所以初始化为-1)

AC代码

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n';
typedef long long ll;
typedef pair<int, int>PII;

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    string str;
    cin >> str;
    int a[26];
    memset(a, -1, sizeof a);
    ll res = 0;
    int n = str.size();
    for (int i = 0; i < n; i++)
    {
        res += (i - a[str[i]-'a']) * (n - i);
        a[str[i] - 'a'] = i;
    }
    cout << res << endl;
    return 0;
}