leetcode--第828题--困难--《统计子串中的唯一字符》

题目:

我们定义了一个函数 c o u n t U n i q u e C h a r s ( s ) countUniqueChars(s) countUniqueChars(s)来统计字符串 s 中的唯一字符,并返回唯一字符的个数。

例如:s = “LEETCODE”,则其中"L",“T”,“C”,“O”,"D"都是唯一字符,因为它们只出现一次,所以 c o u n t U n i q u e C h a r s ( s ) = 5 countUniqueChars(s) = 5 countUniqueChars(s)=5

本题将会给你一个字符串 s s s,我们需要返回 c o u n t U n i q u e C h a r s ( t ) countUniqueChars(t) countUniqueChars(t)的总和,其中 t t t s s s的子字符串。输入用例保证返回值为 32 32 32位整数。

注意,某些子字符串可能是重复的,但你统计时也必须算上这些重复的子字符串(也就是说,你必须统计 s s s的所有子字符串中的唯一字符)。

题目概括:遍历 s s s所有的字串 t t t,统计 t t t中仅出现一次的字母的个数,返回这些个数的总和。

示例:

leetcode--第828题--困难--《统计子串中的唯一字符》_第1张图片

思路

计算 s s s每个字符的贡献度,将所有字符贡献度相加即可;贡献度:这个字符会在几个子串中仅出现一次,若会在 n n n个不同的字串中仅出现一次,则贡献度为 n n n

贡献度怎么计算呢?

E A B C E A B C E EABCEABCE EABCEABCE为例子,计算第 2 2 2 E E E的贡献度;

字符: E A B C E A B C E EABCEABCE EABCEABCE
下标:   0   1   2   3   4   5   6   7   8 \ 0 \ 1\ 2\ 3\ 4\ 5\ 6\ 7\ 8  0 1 2 3 4 5 6 7 8

计算 E E E的贡献度,计算以 E E E为中心点,前后能扩展出几个字串即可;扩展的意思是:向前/后拿几个单词作为与 E E E合并为字串,并确保 E E E在拿完单词后在这个字串中的仅出现过一次。

向前可拿 n n n个,向后可拿 m m m个,则以 E E E为中心,可构成的子串( E E E仅出现过一次的字串)为 n × m n\times m n×m个;即 E E E的贡献值为 n × m n\times m n×m

向前扩展
向前扩展 1 1 1位, C E CE CE
向前扩展 2 2 2位, B C E BCE BCE
向前扩展 3 3 3位, A B C E ABCE ABCE
不能扩展到第 4 4 4位。

向后扩展
向前扩展 1 1 1位, E A EA EA
向前扩展 2 2 2位, E A B EAB EAB
向前扩展 3 3 3位, E A B C EABC EABC
不能扩展到第 4 4 4位。

即可向前扩展 3 3 3位,可向后扩展 3 3 3位;以 E E E为中心,可构成的字串为 3 × 3 = 9 3\times 3=9 3×3=9个, E E E的贡献值为 9 9 9.

如何计算扩展的位数呢?
可以看到相邻两个 E E E之间夹着的字符数量就是扩展位数了。字符数量可以使用下标进行计算:

向前扩展的位数=第二个 E E E的下标-前一个 E E E的下标
向后扩展的位数=后一个 E E E的下标-第二个 E E E的下标

所有计算某个字符的贡献度:

  • 找出该字符前一个同字符出现的下标,没有就是 − 1 -1 1,表示在最前面
  • 找出该字符后一个同字符出现的下标,没有就是 n n n,表示在最后面
  • 贡献度=(当前字符下标-前一个字符下标)*(后一个字符下标-当前字符下标)

代码

class Solution {
    public int uniqueLetterString(String s) {
        int n = s.length();//s的长度
        int ans = 0;//返回结果
        Map <Character,ArrayList<Integer>>index = new HashMap<>();//存储每一个字符的位置(下标)
        for(int i = 0;i<n;i++){
            char c = s.charAt(i);
            if(!index.containsKey(c)){
                index.put(c,new ArrayList<Integer>());
                index.get(c).add(-1);//表示这个字符第一次出现,上一次在最前面
            }
            index.get(c).add(i);//记录当前位置
        }

        for(ArrayList<Integer> list:index.values()){//遍历index存储的链表,逐个字母进行计算
            list.add(n);//表示最后一个出现的位置在最后面
            for(int i = 1;i<list.size()-1;i++){
                ans += (list.get(i)-list.get(i-1))*(list.get(i+1)-list.get(i));//计算贡献度
            }
        }  
        return ans;
    }
}

你可能感兴趣的:(leetcode刷题,leetcode,算法,职场和发展)