Total Appeal of A String

Question:

请添加图片描述

思路:

这个题的找each character contribution 的方法很简单. 我简单举个例子.
简单说就是数每个character 出现在几个substring里了.
然后数substring的方法就是character 的左边range✖️右边range.(类似montonic stack)
像这个题的话, 就不需要montonic stack了, 直接一个loop 走遍每个character
然后(i - (-1))✖️(n - i) 就完事了. 就是所有的substring的个数(substring包括了这个character).
然后重复的character怎么处理呢. 首先如果有重复,那么就是说明我们的for loop已经见过一次这个character了. 而且这个character 在以string长度为单位下, 它的所有substring的出现都加了一遍. 那这个重复出现的第二个character. 就不需要再加一遍所有的substring, 只需要增加不包括第一个character的所有substring的个数就行了。举个例子吧这里实在难解释.
for ex, xxaxxaxx
当我们forloop 走到一个char a (index I = 2) 的时候. 我们会算(2 - (-1))✖️( 8 - 2) = 18 就是这个string 里所有包括第一个a的substring 一共有18个. 那么在loop 走到第二个I 的时候, 因为前面index = 2 的时候已经加了所有所有的substring, (然后是包括把第二个a 也算进来的), 就是上面那个(8-2) 那里其实是有把第二个a也包括进去的. 所以这个时候我们再去找包含第二个a的substring个数时, 就可以把那些也包括第一个a的substring排除掉了. ex: axxaxx, 因为前面算第一个a的时候已经加过了.
因为题目问的是number of distinct characters, 所以对于第一个a找出来的axxaxx 和 第二个a照出来的axxaxx 他俩只能算一个.
那么,我们只要算只包括第二个a的substring而不包括第一个a的substring就好了呀。
就是 xxaxx所有的substirng 组合方式. (5 - 2)✖️(8 - 5) = 9种
然后加到结果里就完事了

注意我们还得实时保存每个char最后一次出现的位置. 用来方便出现重复character的时候找left boundary

class Solution {
    public long appealSum(String s) {
        int last[] = new int[26];
        Arrays.fill(last, -1);
        long res = 0, n = s.length();
        for (int i = 0; i < s.length(); ++i) {
            // if char at index i already existed at front, then we only need to add the number of substring (range. think about montonic stack)that this char occured uniquely
            res += (i - last[s.charAt(i) - 'a']) * (n - i);
            last[s.charAt(i) - 'a'] = i;
        }
        return res;
    }
}

再来说一下dp的方法,简直天才。

dp的方法就是。简单点说, 我们一个for loop, 每次增加看一个character时候, 我们要计算一下 当前我们看过的string.如果加上当前我们reach到的这个char, 它会增加几个新的substring。举个例子
如果我的string 是 “abc”
那么当我运行到c的时候, “abc"照比"ab” 新增加了三个新的substring: “c”, “bc”, “abc” 所以我们dp的算法其实就是我要计算每次新增的那些substring里的distinct characters 数量.
然后注意. 这里有个trick, 一旦理解了就很好懂了.

如果我们have"abcd"
读到d的时候, 增加的substring是: “d” -> “dc” -> “dcb” -> “dcba”
我们在读到c的时候. 增加的substring是: “c” -> “cb” -> “cba”
c出现了三次,b两次,a一次.
再往前推, 读到b的时候, 增加的substring是: “b” -> “ba”

发现了什么规律, 当我读到d的时候, 新生成的substring里c 出现的次数, 就是读到c的时候新生成substring的个数。 而读到c时候, 生成的substring里含b的次数就是读到b的时候新生成的substring个数.

那么我们换成直观一点儿看"abcdxxxxxxxxxxx"
当我们每次新增加一个character, 是不是就是相当于从最右边的character,每次往左画一对,逐次长度增加一. 到最后是不是就是 会always出现 “xxxc” -> “xxxcb” -> xxxcba"
那么相当于character a 对后面每一个新增的character的贡献都是1, 因为每新增一个character, 新产生的substring 只有最长length的那个才会包含一个a.然后b的贡献就是2, c的贡献就是3… 类推

那我们就可以总结, 每个character 对后面的贡献就是它的 index+1.

那么知道了这层关系之后, 对于这道题。我们是不是就可以用dp, for loop 我们先把每个char 的 贡献值存在一个array里, 然后 每次走到一个character, 它新产生的substring的distinct characters 数量就是这个character到前面到头所有的character的贡献值的和. 那么我们只要一直加一直加就完事了.

那么重复character怎么处理呢? 也好办!

对于dp的方法, 就算碰到重复的character, 首先算新生成substring的贡献值 也是把从它开始往左 所有的character的贡献值都加起来, 那么这个加的过程中就也加上了先前这个character出现那次产生的贡献值. 就比如说
“abcdefghb”
第一个b 产生的贡献的substring是"ab" “b”. 那么当第二个b读到的时候, 它会产生"bcdefghb" 和 “abcdefghb” 这两个substring是包括"ab" 和 "b"的, 那么因为有两个b 出现, 这俩substring的贡献值分别是 7和8, 但是正常来讲如果没有重复的character, 这两个新产生的贡献值应该是8 和 9嘛.
但是在dp 相加的过程中, 相加的时候是不会考虑有重复字母出现的可能的. 所以加的时候还是按照8 和 9 加的.那最后再减掉不就好了. 减掉的就是第一个b 的贡献值(第一个b新产生substring的个数)

class Solution {
    public long appealSum(String s) {
        int numOfOccuranceInNewSubString[] = new int[26];
        long currAppeal = 0;
        for (int i = 0; i < s.length(); ++i) {
            numOfOccuranceInNewSubString[s.charAt(i) - 'a'] = i + 1;
            for (int appealOfChar: numOfOccuranceInNewSubString)
                currAppeal += appealOfChar;
        }
        return currAppeal;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值