第十一届蓝桥杯软件类省赛第二场——H:子串分值

题目描述

对于一个字符串 SSS,我们定义 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

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

 

思路:这道题不能单纯的用暴力枚举将所有的子串都枚举出来,这样的时间复杂度已经达到O(n^2),就更别谈后面的操作l。后面去网上看了一下别人的思路
不要正向考虑问题,我们可以通过每次当前看到的字符,就分析该字符,从上一次出现到下一次出现之间这个距离,即将原问题转化为求整个字符串中
每个字符对整体的贡献值。
怎么求一个字母对这一列中只包含这个位置字母的贡献值呢?
公式:该字母的贡献值 = (该字母当前出现的位置 - 该字母上一次出现的位置) * (该字母下一次出现的位置 - 该字母当前出现的位置)

总之,这道题不能从常规角度来解决问题就对了!!!

温馨提示:建议初学算法的朋友们,如果用一种正常思路解决不出来,就要跳出该题,从另一个角度切人(通常这个角度都是一般人想不到,需要大量的刷题和对问题的分析能力等诸多因素)

 

下面给出Java的AC代码:

该代码是参考网友的C++代码改写的,自己真的很难想出来。

 1 public class SubStrGoal {
 2     private static int N = 100000;
 3 
 4     // 存储第i个位置字符上一次出现该字符的位置
 5     private static int[] pre = new int[N + 10];
 6 
 7     // 存储第i个位置字符下一次出现该字符的位置
 8     private static int[] late = new int[N + 10];
 9 
10     // 用于实时更新每个字母最近一次出现的位置,给字母编号:a - 0 、b - 1、 c - 2 ...以此类推
11     private static int[] cur = new int[26];
12     public static void main(String[] args) {
13         Scanner input = new Scanner(System.in);
14         String data = input.next();
15         f(data.toCharArray());
16     }
17 
18     private static void f(char[] data) {
19         int n = data.length;
20 
21         // 初始化cur数组
22         for (int i = 0; i < 26; i++) {
23             cur[i] = -1;
24         }
25 
26         // 处理pre数组
27         for (int i = 0; i < n; i++) {
28             int index = data[i] - 'a';
29             // 更新pre值
30             pre[i] = cur[index];
31             cur[index] = i;
32         }
33 
34         // 重新重置一下cur数组
35         for (int i = 0; i < 26; i++) {
36             cur[i] = n;
37         }
38 
39         // 处理late数组,这里需要从后往前遍历,统计起来比较方便
40         for (int i = n - 1; i >= 0; i--) {
41             int index = data[i] - 'a';
42             // 更新late值
43             late[i] = cur[index];
44             cur[index] = i;
45         }
46 
47         long ans = 0;
48         for (int i = 0; i < n; i++) {
49             ans += (long) (i - pre[i]) * (late[i] - i);
50         }
51         System.out.println(ans);
52     }
53 }
posted @ 2021-03-22 23:23  没有你哪有我  阅读(140)  评论(0编辑  收藏  举报