子串分值
题目描述
对于一个字符串 S,我们定义 S 的分值 f(S) 为 S 中恰好出现一次的字符的个数。例如 f(aba)=1,f(abc)=3,f(aaa)=0。
现在给定一个字符串 S0⋯n−1(长度为 n,1≤n≤10^5),请你计算对于所有 S 的非空子串 Si⋯j(0≤i≤j<n),f(Si⋯j) 的和是多少。
输入描述
输入一行包含一个由小写字母组成的字符串 S。
输出描述
输出一个整数表示答案。
输入
ababc
输出
21
“贡献值法”:计算每个字母的"贡献"
注意:
- 结果用long long型来存储结果
- 这题只有复杂度为才能通过所有测试点
贡献值计算:(当前位置-前一个重复位置)*(后一个重复位置-当前位置)
f(a)=(1-0)*(3-1)=2 : a,ab
f(b)=(2-0)*(4-2)=4: ab,aba,b,ba
f(a)=(3-1)*(6-3)=6: babc,bab,ba,a,ab,abc
f(b)=(4-2)*(6-4)=4: abc,ab,b,bc
f(c)=(5-0)*(6-5)=5: ababc,babc,abc,bc,c
共:2+4+6+4+5=21
代码实现:
#include<iostream>
#include<cstring>//memset()
using namespace std;
typedef long long ll;
const int N=1e5+10;
//子串分值
int main()
{
string s;
cin>>s;
ll ans=0,n=s.size();
ll l[N],r[N],vis[27];//字符左,右边相同字符的下标
char a[N];
for(int i=1;i<=n;i++) a[i]=s[i-1];
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++){
int k=a[i]-'a';
l[i]=vis[k];
vis[k]=i;
}
for(int i=0;i<27;i++) vis[i]=n+1;
for(int i=n;i>=1;i--){
int k=a[i]-'a';
r[i]=vis[k];
vis[k]=i;
}
for(int i=1;i<=n;i++){
ans+=(i-l[i])*(r[i]-i);
}
cout<<ans<<endl;
return 0;
}
子串分值和
题目描述
对于一个字符串 S,我们定义 S 的分值 f(S) 为 S 中出现的不同的字符个数。例如 f(aba)=2,f(abc)=3,f(aaa)=1。
现在给定一个字符串 S0⋯n−1(长度为 n,1≤n≤10^5),请你计算对于所有 S 的非空子串 Si⋯j(0≤i≤j<n),f(Si⋯j) 的和是多少。
输入描述
输入一行包含一个由小写字母组成的字符串 S。
输出描述
输出一个整数表示答案。
输入
ababc
输出
28
“贡献值法” (与上同理)
约定:右端点字符不包含在子串中 (即S=ababc,则S[1,3]=ab,下标从1开始)
i: 1 2 3 4 5
S: a b a b c
suf: 3 4 6 6 6
ans = i*(suf[i]-i) =2+4+9+8+5=28
代码:
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e5+10;
int main()
{
string s;
cin>>s;
ll suf[N],vis[27],n=s.size(),ans=0;
char a[N];
for(int i=1;i<=n;i++) a[i]=s[i-1];
for(int i=0;i<27;i++) vis[i]=n+1;
for(int i=n;i>=1;i--){
int k=a[i]-'a';
suf[i]=vis[k];
vis[k]=i;
}
for(int i=1;i<=n;i++)
ans+=i*(suf[i]-i);
cout<<ans<<endl;
return 0;
}