ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[LeetCode]828. Count Unique Characters of All Substrings of a Given String 动态规划转移方程详解

2022-06-04 00:31:11  阅读:336  来源: 互联网

标签:Count 子串 Given String index 位置 characters unique dp


题目描述

LeetCode原题链接:828. Count Unique Characters of All Substrings of a Given String

Let's define a function countUniqueChars(s) that returns the number of unique characters on s.

  • For example, calling countUniqueChars(s) if s = "LEETCODE" then "L""T""C""O""D" are the unique characters since they appear only once in s, therefore countUniqueChars(s) = 5.

Given a string s, return the sum of countUniqueChars(t) where t is a substring of s.

Notice that some substrings can be repeated so in this case you have to count the repeated ones too.

 

Example 1:

Input: s = "ABC"
Output: 10
Explanation: All possible substrings are: "A","B","C","AB","BC" and "ABC".
Every substring is composed with only unique letters.
Sum of lengths of all substring is 1 + 1 + 1 + 2 + 2 + 3 = 10

Example 2:

Input: s = "ABA"
Output: 8
Explanation: The same as example 1, except countUniqueChars("ABA") = 1.

Example 3:

Input: s = "LEETCODE"
Output: 92

 

Constraints:

  • 1 <= s.length <= 105
  • s consists of uppercase English letters only.

思路分析

关于子字符串的动态规划题目一般dp[i]都表示以s[i]结尾的子字符串的某最小值/最大值,这一题也不例外。我们用dp[i]表示以字符s[i]结尾的所有子字符串的unique characters之和。那么最终结果就是dp数组每一项累加的结果。

那么如何寻找状态转移方程呢?每当遍历到一个新的位置时,会有两种状况:1)当前位置的子母之前没有出现过;2)当前位置的子母之前出现过。让我们分别来看。

情况一,如果当前位置的子母之前没有出现过,比如对于字符串ABC,当i=1时,以B结尾的子字符串有B, AB,那么dp[1] = 1 + 2 = 3;当i=2时,因为C之前没有出现过,所以对上一位置的每个子字符串 (B, AB) 的unique characters个数可以分别贡献1 (BC, ABC),再加上C本身就是一个子串,所以dp[2] = 1 + dp[1] + 2 = 6。

情况二,如果当前位置的子母之前出现过,比如对于字符串EABDCA,当i=4时,子串EABDC的unique characters之和为15;当i=5时,会发现A在之前已经出现过了(i=1位置)。那么在上一个A出现之后的位置,即(1, 4]范围内的所有子串是不包含A的,对于这些子串,和情况一相同,新增的A仍然可以为unique characters贡献1;而对于[包含了一个A的那些子串,比如EABDC,新增一个A会另之前出现的A不能再被计入unique charaters,EABDC的unique characters数为5,新增一个A后(EABDCA)unique characters数反而变成了4,A在这里没有起到贡献作用反而抵消了1;不过,如果新增的A作为单独子串看待的话,还是可以贡献1的。于是,dp[5] = 1 + dp[4] + (5 - 2) - (2 - 0) = 17。后面两个括号内的内容分别是新增i=5位置的A贡献的unique charaters数(等于i=5位置之前不含第一个A的子串个数)和抵消的unique charaters数(等于i=5位置之前包含了第一个A的子串个数),两者都是通过位置下标计算得到的;贡献的个数是当前位置减去上一个A出现位置(i = 1)的后一位置,抵消的个数是上一个A出现位置后一位减去上上一个A出现位置后一位(例子中因为之前只出现过一个A,所以上上一个A出现的位置可以看作0)。补充一下,因为子串一定是连续的,所以对于[0,i]范围内以s[i]为结尾的长度大于1的子串(即不包含s[i]自身单独形成的子串)个数就等于下标位置i。

可见,在遍历过程中我们还需要记录每个字母前两次出现的位置。题目中限制了子母范围是26个大写子母,为了提高效率和方便计算,我们可以用两个长度为26的数组pre[26]和prePre[26]分别记录某一子母上一次出现位置的后一位置和上上一次出现位置的后一位置。

综合前面的分析,我们可以得出状态转移方程为:

 

 

如果还是不大好理解,我们来看一个例子,对于字符串GACEABDCA:

 

 

到这里应该就很清晰了吧,如果还是困惑,可以自己举几个不同的例子手动演算一下。

代码示例(JS)

 1 var uniqueLetterString = function(s) {
 2     let pre = new Array(26), prePre = new Array(26);
 3     pre.fill(0);
 4     prePre.fill(0);
 5     let res = 0, cur = 0;
 6     for(let i = 0; i < s.length; i++) {
 7         let index = s[i].charCodeAt(0) - 'A'.charCodeAt(0);
 8         cur = cur + 1 + (i - pre[index]) - (pre[index] - prePre[index]);
 9         res += cur;
10         prePre[index] = pre[index];
11         pre[index] = i + 1;
12     }
13     return res;
14 };

标签:Count,子串,Given,String,index,位置,characters,unique,dp
来源: https://www.cnblogs.com/barryyeee/p/16339850.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有