1525 字符串的好分割数目(字典计数、动态规划)

1. 问题描述:

给你一个字符串 s ,一个分割被称为 「好分割」 当它满足:将 s 分割成 2 个字符串 p 和 q ,它们连接起来等于 s 且 p 和 q 中不同字符的数目相同。请你返回 s 中好分割的数目。

示例 1:

输入:s = "aacaba"
输出:2
解释:总共有 5 种分割字符串 "aacaba" 的方法,其中 2 种是好分割。
("a", "acaba") 左边字符串和右边字符串分别包含 1 个和 3 个不同的字符。
("aa", "caba") 左边字符串和右边字符串分别包含 1 个和 3 个不同的字符。
("aac", "aba") 左边字符串和右边字符串分别包含 2 个和 2 个不同的字符。这是一个好分割。
("aaca", "ba") 左边字符串和右边字符串分别包含 2 个和 2 个不同的字符。这是一个好分割。
("aacab", "a") 左边字符串和右边字符串分别包含 3 个和 1 个不同的字符。

示例 2:

输入:s = "abcd"
输出:1
解释:好分割为将字符串分割成 ("ab", "cd") 。

示例 3:

输入:s = "aaaaa"
输出:4
解释:所有分割都是好分割。

示例 4:

输入:s = "acbadbaada"
输出:2

提示:

  • s 只包含小写英文字母。
  • 1 <= s.length <= 10^5

2. 思路分析:

① 分析题目可以知道我们需要计算[0, i]与[i +1, len(s)]的不同字符的个数,所以这是一个计数问题,对于计数的相关问题,是可以使用哈希表记录键值对的映射关系进行计数,也就是将出现相同的键进行计数,java或者c++语言可以使用map进行计数,对于python语言可以使用字典dict进行计数,只是计数的时候使用的数据结构是不一样的,但是作用效果是一样的,基于这个想法比较容易想到的是使用两个字典来记录左边与右边出现的字符的数目,所以可以先预处理一下这两个字典dic1与dic2,dic1记录字符串的第一个字符出现的次数,dic2记录字符串中第二个字符到len(s)位置字符的出现的次数,然后从字符串s的第二个位置开始模拟字符串的好分割过程,对于当前的字符c,dic1[c] += 1, dic2[c] -= 1,并且需要注意的是当前字符c在字典dic2出现的次数为1的时候那么就需要删除掉dic2中的这个键,这样我们才可以根据两个字典的长度判断出不同字符的数目,我们在判断出满足两个字典长度相同的时候对结果进行加1即可

② 所以我们两遍遍历字符串 + 两个字典来长度检查不同字符的数目

③ 除了上面的这个解法之外,力扣官方提供了一个动态规划的解法,我感觉这个思路也是蛮好的,容易理解,可以学习学习,我的理解如下:它是有一种递推的思想在里面,这个其实也是动态规划能够解决的问题的一个特点,首先声明两个长度为26的列表(相当于是字典)对出现过的字母进行标记(这样也是为了后面的的计数方便),我们在遍历字符串的过程中由前一个位置不同字符出现的次数推出当前这个位置不同字符出现的次数,可以先判断当前字符在字母表中对应的位置是否出现过,如果未出现过那么在上一个位置的不同字符出现的次数进行加1即可,假如出现过那么维持上一个的不同字符出现的次数,这样一遍遍历字符串s就可以知道从0到字符串末尾的位置上不同字符出现的次数,因为是比较左右两边的情况,所以自然想到从末尾的位置开始以同样的方法记录不同字符出现的次数,这样两次遍历之后才可以比较左右两边的不同字符串的次数

3. 代码如下:

import collections


class Solution:
    def numSplits(self, s: str) -> int:
        if not s: return 0
        dic1, dic2 = collections.defaultdict(int), collections.defaultdict(int)
        dic1[s[0]] += 1
        for i in range(1, len(s)):
            dic2[s[i]] += 1
        res = 0
        if len(dic1) == len(dic2): res += 1
        for i in range(1, len(s) - 1):
            dic1[s[i]] += 1
            # 当检查当前待删除的字符在dic2中的次数为1的时候应该将dic2中对应的键删除掉这样在统计长度的时候才不会统计进来
            if dic2[s[i]] == 1: del dic2[s[i]]
            if len(dic1) == len(dic2): res += 1
        return res

官方动态规划代码:

class Solution:
    def numSplits(self, s: str) -> int:
        n = len(s)
        left, right = [0] * (n + 2), [0] * (n + 2)

        rec_left = [False] * 26
        rec_right = [False] * 26
        for i in range(1, n + 1):
            c = ord(s[i - 1]) - ord("a")
            if rec_left[c]:
                left[i] = left[i - 1]
            else:
                rec_left[c] = True
                left[i] = left[i - 1] + 1
        
        for i in range(n, 0, -1):
            c = ord(s[i - 1]) - ord("a")
            if (rec_right[c]):
                right[i] = right[i + 1]
            else:
                rec_right[c] = True
                right[i] = right[i + 1] + 1
        
        ret = sum(1 for i in range(1, n) if left[i] == right[i + 1])
        return ret

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值