美文网首页LeetCode算法
1915-最美子字符串的数目-另类前缀和

1915-最美子字符串的数目-另类前缀和

作者: 华雨欣 | 来源:发表于2021-06-27 19:10 被阅读0次

    写在前面

    247场周赛第三题,没想到使用前缀和,看到大佬们十几行就做完了真的佩服。本文主要讲解思路,并配以完整代码供参考。

    题目


    最近力扣题目翻译的真是越来越晦涩了,比赛的时候看了半天才看懂啥事最美字符串。通俗来说,统计某字符串中各种字符出现的次数,其中,出现次数为奇数次的字符不超过一个,则称该字符串为最美字符串

    核心思路

    最简单直接的思路:枚举每个子串,并判断是否为最美字符串,统计答案。

    判断是否为最美字符串

    一种朴素的思想,使用一个cnts数组,存储每种字符出现的次数,然后遍历cnts数组判断是否奇数不超过一个即可。不过,题目中给出字符串只由前十个小写英文字母组成,并且只关注字符出现的是奇数次还是偶数次(只由两种选择),所以使用一个10bit的二进制数来记录一个字符串的状态还是很容易想到的,只要使用bitCount得到结果小于等于1,则该字符串为最美字符串。

    问题

    如果通过枚举+判断的方式统计结果,单纯枚举所有子串的时间复杂度为O(N ^ 2),已经无法满足10 ^ 5的数据量,更不用说遍历字符串得到是否为最美字符串了。

    解决方案

    既然枚举子串不能解决问题,肯定要将某个区间中的结果一次性计算出来,而常见的区间处理的方式就是前缀和了。我们不妨使用sum[i]表示前i个字符组成的子串的状态(即上述对应的二进制数),那么什么样的sum[i]和sum[j](j < i)是有关系的呢?我们通过下图理解。


    可以看到状态值相同的两个状态中间可以形成一个答案,状态值有一位不同的两个状态之间也可以形成一个答案。而题目中只需要求出最美子字符串的个数即可,那么,我们不妨使用一个数组存储遍历到位置i时,状态sum出现次数,就可以得到如下的状态转移:
    res += cnts[sum]
    for(int i = 0; i < 10; i++){
        res += cnts[sum ^ (1 << i)];
    }
    

    这样我们就可以得到完整代码了。

    完整代码

    class Solution {
        public long wonderfulSubstrings(String word) {
            int sum = 0;
            int[] cnts = new int[1 << 10];
            cnts[0] = 1;
            long res = 0;
    
            for(char c : word.toCharArray()){
                sum = sum ^ (1 << (c - 'a'));
                res += cnts[sum];
                for(int i = 0; i < 10; i++){
                    res += cnts[sum ^ (1 << i)];
                }
                cnts[sum]++;
            }
            return res;
        }
    }
    

    代码很短,也不难理解,最核心的思路就是通过前缀和得到区间中最美子字符串的个数,这里还是需要反复揣摩。
    如果文章有任何问题,还请指出。
    感恩相遇~~~

    相关文章

      网友评论

        本文标题:1915-最美子字符串的数目-另类前缀和

        本文链接:https://www.haomeiwen.com/subject/ecvxultx.html