题目描述
对于一个字符串 SS,我们定义 SS 的分值 f(S)f(S) 为 SS 中恰好出现一次的字符个数。例如 f(aba) = 1,f(abc) = 3, f(aaa) = 0f(aba)=1,f(abc)=3,f(aaa)=0。
现在给定一个字符串 S_{0 \cdots n − 1}S0⋯n−1(长度为 nn,1 \leq n \leq 10^51≤n≤105),请你计算对于所有 SS 的非空子串 S_{i \cdots j}(0 ≤ i ≤ j < n)S**i⋯j(0≤i≤j<n),f(S_{i \cdots j})f(S**i⋯j) 的和是多少。
输入描述
输入一行包含一个由小写字母组成的字符串 SS。
输出描述
输出一个整数表示答案。
输入输出样例
示例
输入
ababc
输出
21
运行限制
- 最大运行时间:1s
- 最大运行内存: 256M
暴力法 会超时
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
static int result = 0;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String S = scan.nextLine();
//两层循环
for(int i = 0; i < S.length(); i++){
for(int j = i; j < S.length(); j++){
//substring左闭右开
countNum(S.substring(i,j+1));
}
}
System.out.println(result);
scan.close();
}
//使用哈希表统计出现1次的字母
static void countNum(String S){
int[] array = new int[26];
for(int i = 0; i < S.length(); i++){
char c = S.charAt(i);
array[c - 'a']++;
}
for(int i = 0; i < 26; i++){
if(array[i] == 1) result++;
}
}
}
参考题解的做法
算贡献度
原理:只有当字母个数在子串中个数为1才会有贡献度。
分析字母A的左右两边出发,分别计算移动了多远才会出现一个字母与A相同,然后停止遍历,记录下步长left和right。
贡献度 = (left+1) * (right+1)
以bacbacdb为例,我们对第四个字母b进行分析:
- 先往左边遍历,可以移动两个单位,则left = 2;
- 再往右边遍历,可以移动3个单位,则right = 3;
- 则对于b字母,有贡献度的部分为acbacd,满足的字串(注意要有b)有:
- 从左边第一个字母a开始,分别是acb,acba,acbac,acbacd,有4个
- 然后左边从第二个字母c开始,为cb,cba,cbac,cbacd,有4个
- 从左边第三个字母b开始,为b,ba,bac,bacd,有4个
- 12 = (2+1) * (3+1)
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
static int result = 0;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String S = scan.nextLine();
char[] array = S.toCharArray();
int len = array.length;
for(int i = 0; i < len; i++){
int left = 0;
int right = 0;
for(int j = i - 1; j >= 0 && array[j] != array[i]; j--){
left++;
}
for(int j = i + 1; j < len && array[j] != array[i]; j++){
right++;
}
result += (left + 1) * (right + 1);
}
System.out.println(result);
scan.close();
}
}