子域名访问计数

标签: 数组 哈希表 字符串 计数

难度: Medium

网站域名 "discuss.leetcode.com" 由多个子域名组成。顶级域名为 "com" ,二级域名为 "leetcode.com" ,最低一级为 "discuss.leetcode.com" 。当访问域名 "discuss.leetcode.com" 时,同时也会隐式访问其父域名 "leetcode.com" 以及 "com"

计数配对域名 是遵循 "rep d1.d2.d3""rep d1.d2" 格式的一个域名表示,其中 rep 表示访问域名的次数,d1.d2.d3 为域名本身。

  • 例如,"9001 discuss.leetcode.com" 就是一个 计数配对域名 ,表示 discuss.leetcode.com 被访问了 9001 次。

给你一个 计数配对域名 组成的数组 cpdomains ,解析得到输入中每个子域名对应的 计数配对域名 ,并以数组形式返回。可以按 任意顺序 返回答案。

示例 1:

输入:cpdomains = ["9001 discuss.leetcode.com"]
输出:["9001 leetcode.com","9001 discuss.leetcode.com","9001 com"]
解释:例子中仅包含一个网站域名:"discuss.leetcode.com"。
按照前文描述,子域名 "leetcode.com" 和 "com" 都会被访问,所以它们都被访问了 9001 次。

示例 2:

输入:cpdomains = ["900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org"]
输出:["901 mail.com","50 yahoo.com","900 google.mail.com","5 wiki.org","5 org","1 intel.mail.com","951 com"]
解释:按照前文描述,会访问 "google.mail.com" 900 次,"yahoo.com" 50 次,"intel.mail.com" 1 次,"wiki.org" 5 次。
而对于父域名,会访问 "mail.com" 900 + 1 = 901 次,"com" 900 + 50 + 1 = 951 次,和 "org" 5 次。

提示:

  • 1 <= cpdomain.length <= 100
  • 1 <= cpdomain[i].length <= 100
  • cpdomain[i] 会遵循 "repi d1i.d2i.d3i""repi d1i.d2i" 格式
  • repi 是范围 [1, 104] 内的一个整数
  • d1id2id3i 由小写英文字母组成

Submission

运行时间: 27 ms

内存: 15.9 MB

class Solution:
    def subdomainVisits(self, cpdomains: List[str]) -> List[str]:
        cnt = Counter()
        for domain in cpdomains:
            c, s = domain.split()
            c = int(c)
            cnt[s] += c
            while '.' in s:
                s = s[s.index('.') + 1:]
                cnt[s] += c
        return [f"{c} {s}" for s, c in cnt.items()]

Explain

题解的核心思路是使用哈希表(字典)来记录每个域名及其子域名的访问次数。对于每个输入的域名配对,首先将次数和域名分离,然后添加该域名的访问次数到哈希表。接着通过循环移除域名的最左部分(直到没有'.'为止),不断更新哈希表中对应子域名的访问次数。最后,将哈希表中的数据格式化为所需的输出格式。

时间复杂度: O(nk)

空间复杂度: O(m)

class Solution:
    def subdomainVisits(self, cpdomains: List[str]) -> List[str]:
        cnt = Counter()  # 创建计数器用于统计域名访问次数
        for domain in cpdomains:
            c, s = domain.split()  # 分离次数和域名
            c = int(c)  # 将次数转换为整数
            cnt[s] += c  # 对当前域名计数
            while '.' in s:  # 处理子域名
                s = s[s.index('.') + 1:]  # 移除最左部分域名
                cnt[s] += c  # 更新子域名计数
        return [f"{c} {s}" for s, c in cnt.items()]  # 格式化输出结果

Explore

从左到右逐步移除最左部分的域名来更新哈希表的方法可以确保所有可能的子域名都被考虑在内。这种方法通过从整个域名开始,逐步通过移除最前面的部分来获取所有下级域名,可以保证从最具体的域名到最广泛的域名的顺序。这样可以有效地使用循环来捕获和计数所有子域名的访问次数,而不需要额外的复杂逻辑或递归处理。

在分割域名时,使用标准的字符串操作如索引查找'.'来判断分割点,这是根据域名规范来进行的。为了确保不遗漏或错误处理,程序中应该严格按照域名结构规则来操作,考虑到所有域名都是由多个用点('.')分隔的部分组成。此外,进行任何字符串操作前,应检查字符串的有效性,如非空检查,以及确认有分隔符存在。这些措施可以帮助避免处理错误或非标准的域名结构。

在Python中,整数类型是动态的,可以根据需要自动扩展到较大的数值,因此通常不必担心常规整数运算中的溢出问题。然而,对于非常大的数据集或在其他编程语言中,应考虑使用大数处理库或数据类型,如Java中的BigInteger。此外,还可以通过适时的数据聚合和压缩,以及在处理前对数据进行分片或批处理来降低内存压力和避免运算时的潜在溢出。

在最后格式化输出结果时,将域名和访问次数反转输出是为了符合题目要求的输出格式,即'访问次数 域名'的形式。这种格式更直观地反映了每个域名的访问量,同时也方便了结果的阅读和后续的数据处理,如排序或进一步的分析。这是一种常见的数据表示方式,特别是在需要突出显示数量或统计数据时。