字符贡献度问题

字符贡献度问题

一、问题引入

对于一个字符串,如ABA,求出其中只出现一次的字符的个数如何求取?easy,哈希表统计一下就好。

那么,对于ABA的所有子串,其中每个子串中只出现一次的字符个数之和如何求取?


二、问题分析

ABA为例,一般的暴力算法是:求出所有子串,即A,B,A,AB,BA,ABA,然后对于每个子串,都使用哈希表计算只出现一次的字符个数。

很明显,该算法的时间复杂度为:
[ C ( N , 1 ) + C ( N , 2 ) + C ( N , 3 ) + . . . + C ( N , N ) ] ∗ N = N ∗ ( 2 N − 1 ) [C(N,1)+C(N,2)+C(N,3)+...+C(N,N)] * N = N * (2^N - 1) [C(N,1)+C(N,2)+C(N,3)+...+C(N,N)]N=N(2N1)
有没有更好的方法?

我们不妨考虑:什么时候子串的某个字符是唯一的?设原字符串为s[0, n),子串为s[l, r],出现一次的字符所在下标为i。那么表明,该字符上次出现的下标必然在l之前,下次出现的下标必然在r之后,因此才能保证[l, r]中该字符只出现一次。

那么我们再换一个角度:假设字符c上次出现的下标为Li,下次出现的下标为Ri,则对于s[Li, Ri]内的所有子串,字符c都能贡献一个只出现一次的次数,对否?那么整个问题就变成了,s[Li, Ri]内有多少个子串包含字符c!

我们以字符串AXABCA为例,计算该字符串中有多少个子串包含i=2处的字符A:

image-20220906094926364

已知,s[1, 4]只包含一个字符A,我们可以将该字符串分为三部分:[1, 1], 2, [3, 4],其中[1, 1]和[3, 4]不包含A。那么包含A的子串可以从[1, 1]和[3, 4]中抽取,但是必须保证[1, 1]中抽取的字符串必须以1结尾,因为它要与i=2处的A相连。同样的,[3, 4]中抽取的字符串必须以3开头,因为它要与i=2处的A相连。

所以,[1, 1]可以抽取s[1,1]、空串,[3, 4]可以抽取空串、s[3, 3]、s[3, 4],总计2*3=6个子串。

那么从代数的角度,子串的个数就是[i - Li]*(Ri - i)

因此,对于字符串中的每一个字符,我们求出它能为多少个子串贡献只出现一次的次数,就相当于变相地求解了整个复杂的问题了!


三、代码实现

题目出自LeetCode 828. 统计子串中的唯一字符

int uniqueLetterString(string s) 
{
    // 记下标i处的字符c左边出现c的下标为i1,右边出现c的下标i2
    // XBAXCA   i1=-1 i=2 i2=5  
    // 则该字符为(i-i1)*(i2-i)个子串贡献一个唯一字符
    int n = s.size();
    vector<int> dic(26, -1);
    vector<int> left(n); // 记录每个字符,左边出现同样字符的最近下标
    vector<int> right(n); // 记录每个字符,右边出现同样字符的最近下标
    for (int i = 0; i < n; ++i)
    {
        left[i] = dic[s[i] - 'A'];
        dic[s[i] - 'A'] = i;
    }
    dic = vector<int>(26, n);
    for (int i = n - 1; i >= 0; --i)
    {
        right[i] = dic[s[i] - 'A'];
        dic[s[i] - 'A'] = i;
    }

    int res = 0;
    for (int i = 0; i < n; ++i)
    {
    	res += (i - left[i]) * (right[i] - i);
    }

    return res;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白龙码~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值