问题描述
对于一个字符串 ,我们定义 的分值 为 中恰好出现一次的字符个数。例如 “aba”,“abc”, “aaa”。
现在给定一个字符串 (长度为 ),请你计算对于所有 的非空子串 ,的和是多少。
输入格式
输入一行包含一个由小写字母组成的字符串 。
输出格式
输出一个整数表示答案。
样例输入
ababc
样例输出
21
样例说明
子串 f值
a 1
ab 2
aba 1
abab 0
ababc 1
b 1
ba 2
bab 1
babc 2
a 1
ab 2
abc 3
b 1
bc 2
c 1
暴力写法
这题一开始我只会用暴力的方法做,拿了50/100,但最后五个测试点还是没有过去,因此我们需要更优化的方法。鉴于理解需要由浅入深,以下是暴力骗分的代码:
#include<bits/stdc++.h>
using namespace std;
char s[10000005];
int f(int bg, int ed)
{
int cal[26] = {0};
int sum = 0;
for(int i = bg; i <= ed; i++)
cal[s[i]-'a']++;
for(int i = 0; i < 26; i++)
if(cal[i] == 1)
sum++;
return sum;
}
int main()
{
cin >> s;
int sum = 0;
int len = strlen(s);
for(int i = 0; i < len; i++)
{
for(int k = 0; k < len-i; k++)
sum += f(i, i+k);
}
printf("%d\n", sum);
return 0;
}
AC思路
为了方便处理字符串,在输入后我们在其前面增加了一个‘0’,使得添加后的原字符串是从新字符串下标1开始的。于是对修改后的字符串依次进行遍历,假设当前遍历到的下标为i,查找前一个与当前字符相等的下标prei和后一个与当前字符相等的下标nxti。(当前字符若无前一个出现的值,则将前一个出现的下标设置为0,同理无后一个出现的值,则将下标设置为字符串的size())
从题目的角度出发,要求寻找只出现一次的字串字符个数,我们不难发现,下标为i的字符对结果的贡献值是(i-prei)*(nxti-i),最后对贡献值遍历相加即可。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+5;
int pre[N], nxt[N], a[26];
int main()
{
string s;
cin >> s;
s = '0' + s;
ll t;
for(ll i = 1; i < s.size(); i++)
{
pre[i] = a[s[i]-'a'];
a[s[i]-'a'] = i;
}
for(int i = 0; i < 26; i++)
a[i] = s.size();
for(ll i = s.size()-1; i >= 1; i--)
{
nxt[i] = a[s[i]-'a'];
a[s[i]-'a'] = i;
}
ll sum = 0;
for(ll i = 1; i < s.size(); i++)
sum += (ll)((i-pre[i]) * (nxt[i]-i));
cout << sum << endl;
return 0;
}