828. 统计子串中的唯一字符|动态规划X|数学思维|转换角度

在这里插入图片描述

一、动态规划
1、我的思路
dp记录以i为结尾的所有子串中的唯一字符的总数,答案res就是将dp数组求和,但超时了。
提交一:

class Solution:
    def uniqueLetterString(self, s: str) -> int:
        nlen = len(s)
        dp = [0] * nlen # 记录以i为结尾的所有子串中的唯一字符的总数
        assist = [[0] * 26 for _ in range(nlen)] # 记录 i 到当前遍历位置为止的子串中,有哪些字符

        
        res = 0 
        for i in range(nlen):
            
            tmp = 0
            assist[i][ord(s[i]) - 65] += 1 
            # 初始状态
            if i == 0:
                dp[i] = 1
            else:
                for j in range(i): # 遍历以i结尾的所有子串
                    if assist[j][ord(s[i]) - 65] == 0: # 新字符不会给该子串带来重复字符
                        tmp += 1 # 该子串在前一个dp的结果上,贡献1个唯一字符
                    elif assist[j][ord(s[i]) - 65] == 1: # 带来重复
                        tmp -= 1 # 该子串在前一个dp的结果上,去掉1个唯一字符
                    # else: # 带来重复,但已经在前一个dp的结果中去掉过了
                    #     pass # 则不需要变动

                    assist[j][ord(s[i]) - 65] += 1 # 辅助数组更新 
                    print("tmp:", tmp)

                    # 似乎不需要记录有多少个某字符
                dp[i] = dp[i-1] + tmp + 1 # 加上这个一个字符的子串
            
            res += dp[i]

            print("assist函数:", assist)
            print("dp函数:", dp)
        
        return res

把数组改成用两个变量,还是超时了。
提交二:

class Solution:
    def uniqueLetterString(self, s: str) -> int:
        nlen = len(s)
        # dp = [0] * nlen # 记录以i为结尾的所有子串中的唯一字符
        pre = 0
        cur = 0
        assist = [[0] * 26 for _ in range(nlen)] # 记录 i 到当前遍历位置为止的子串中,有哪些字符

        # 初始状态
        # dp[0] = 1 
        # assist[0]
        res = 0
        for i in range(nlen):
            
            tmp = 0
            assist[i][ord(s[i]) - 65] += 1 
            if i == 0:
                # dp[i] = 1
                # pre = 0
                cur = 1
                pre = cur
            else:
                for j in range(i): # 遍历以i结尾的所有子串
                    if assist[j][ord(s[i]) - 65] == 0: # 新字符不会给该子串带来重复字符
                        tmp += 1 # 该子串在前一个dp的结果上,贡献1个唯一字符
                    elif assist[j][ord(s[i]) - 65] == 1: # 带来重复
                        tmp -= 1 # 该子串在前一个dp的结果上,去掉1个唯一字符
                    else: # 带来重复,但已经在前一个dp的结果中去掉过了
                        pass # 则不需要变动

                    assist[j][ord(s[i]) - 65] += 1 # 辅助数组更新 
                    print("tmp:", tmp)

                    # 似乎不需要记录有多少个某字符
                # dp[i] = dp[i-1] + tmp + 1 # 加上这个一个字符的子串
                cur = pre + tmp + 1
                pre = cur
                
                
            
            # res += dp[i]
            res += cur

            print("assist函数:", assist)
            # print("dp函数:", dp)
        
        return res




2、答案思路
在这里插入图片描述

我的理解:
这个方法是遍历每个下标的字符char,统计出【只包含一个该字符char的所有子串】,这个数量就是该字符char能贡献给最终答案的【所有子串的独特字符数量】。 将题目【s的所有子串中唯一字符的数量有多少】转换为【s中的每个字符是唯一字符的子串有多少】

class Solution:
    def uniqueLetterString(self, s: str) -> int:
        LETTER = collections.defaultdict(list)
        for i, char in enumerate(s):
            LETTER[char].append(i) # 记录每个字母的出现位置的所有下标

        res = 0
        for letter in LETTER.values(): # 遍历每个出现的字母,获得其所有下标
            letter = [-1] + letter + [len(s)] # 补足前后两断点,方便统一计算
            for index in range(1, len(letter)-1): # 遍历每个下标,除前后两端
                res += (letter[index] - letter[index-1]) * (letter[index+1] - letter[index])

        return res


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值