对于一个字符串 S,我们定义 S 的分值 f(S) 为 S 中恰好出现一次的字符个数。
例如 f(“aba”)=1,f(“abc”)=3, f(“aaa”)=0
现在给定一个字符串 S[0…n−1](长度为 n),请你计算对于所有 S 的非空子串 S[i…j](0≤i≤j<n), f(S[i…j]) 的和是多少。
输入格式
输入一行包含一个由小写字母组成的字符串 S。
输出格式
输出一个整数表示答案。
数据范围
对于 20%20% 的评测用例,1≤n≤101≤10;
对于 40%40% 的评测用例,1≤n≤1001≤≤100;
对于 50%50% 的评测用例,1≤n≤10001≤≤1000;
对于 60%60% 的评测用例,1≤n≤100001≤≤10000;
对于所有评测用例,1≤n≤1000001≤≤100000。
输入样例:
ababc
输出样例:
21
样例说明
所有子串 f值如下:
a 1
ab 2
aba 1
abab 0
ababc 1b 1ba 2bab 1babc 2a 1ab 2abc 3b 1bc 2c 1
思路:
对于一个字符串***a**a***a,对于中间的a,我们可以看出到左边的a之间我们可以切割出3个只含1个a的子字符串。同理对于右边能切割出4个。也就是说在两个a之间的a能够切割出3*4个组合。也就是说这个a对于整体的贡献值就是3*4。
同理,我们只需要求出每个元素与之前之后元素的距离,就能求出整体的贡献值也就是答案。
如果左边没有元素或者右边没有元素,我们就需要初始化左边和右边的值分别是0和len了。两边的长度也就是 i -l 与 r - i。
为了区分左边第一个元素是第一个字母还是原字符串中没有之前出现该字母,我们从1开始记录。同理之后的数组是从0开始记录但是最终元素多一位。因此开的空间为len+1
代码:
import java.util.*;
public class Main{public static void main(String[] args) {Scanner sc = new Scanner(System.in);String str = sc.nextLine();sc.close();int len = str.length();int[] l = new int[len+1];int[] r = new int[len+1];int[] h = new int[26];for (int i = 1; i < l.length; i++) {int t = str.charAt(i-1) - 'a';l[i] = h[t];h[t] = i;}for (int i = 0; i < 26; i++) {h[i] = len;}for (int i = r.length-2; i >= 0; i--) {int t = str.charAt(i) - 'a';r[i] = h[t];h[t] = i;}long ans = 0l;for (int i = 0; i < len; i++) {ans += (long) (i+1 - l[i+1])*(r[i] - i);}System.out.println(ans);}
}