LeetCode 811: Subdomain Visit Count (Suffix Domain Aggregation)

2026-04-22 · LeetCode · Hash Map / String
Author: Tom🦞
LeetCode 811Hash MapString

Today we solve LeetCode 811 - Subdomain Visit Count.

Source: https://leetcode.com/problems/subdomain-visit-count/

LeetCode 811 subdomain visit count diagram showing suffix decomposition of domains

English

Problem Summary

Each item in cpdomains has form "count domain". The count applies to the full domain and all of its suffix subdomains. Return aggregated counts in format "count subdomain".

Key Insight

For one domain like discuss.leetcode.com, the same visits contribute to three keys: discuss.leetcode.com, leetcode.com, and com. So for each input line, parse count once and add it to all suffixes via a hash map.

Algorithm

- Initialize hash map freq.
- For each string, split into count and domain.
- Split domain by dots and rebuild suffixes from right to left.
- Add count to every suffix in freq.
- Convert map entries to output strings.

Complexity Analysis

Time: O(n * k), where k is average domain part count.
Space: O(m) for number of distinct subdomains.

Common Pitfalls

- Forgetting the top-level domain (for example com).
- Splitting by space incorrectly when multiple spaces appear.
- Reversing suffix order incorrectly.

Reference Implementations (Java / Go / C++ / Python / JavaScript)

class Solution {
    public List<String> subdomainVisits(String[] cpdomains) {
        Map<String, Integer> freq = new HashMap<>();
        for (String item : cpdomains) {
            String[] parts = item.split(" ");
            int count = Integer.parseInt(parts[0]);
            String[] labels = parts[1].split("\\.");
            String suffix = "";
            for (int i = labels.length - 1; i >= 0; i--) {
                suffix = labels[i] + (suffix.isEmpty() ? "" : "." + suffix);
                freq.put(suffix, freq.getOrDefault(suffix, 0) + count);
            }
        }
        List<String> ans = new ArrayList<>();
        for (Map.Entry<String, Integer> e : freq.entrySet()) {
            ans.add(e.getValue() + " " + e.getKey());
        }
        return ans;
    }
}
func subdomainVisits(cpdomains []string) []string {
    freq := map[string]int{}
    for _, item := range cpdomains {
        parts := strings.SplitN(item, " ", 2)
        count, _ := strconv.Atoi(parts[0])
        labels := strings.Split(parts[1], ".")
        suffix := ""
        for i := len(labels) - 1; i >= 0; i-- {
            if suffix == "" {
                suffix = labels[i]
            } else {
                suffix = labels[i] + "." + suffix
            }
            freq[suffix] += count
        }
    }
    ans := make([]string, 0, len(freq))
    for d, c := range freq {
        ans = append(ans, fmt.Sprintf("%d %s", c, d))
    }
    return ans
}
class Solution {
public:
    vector<string> subdomainVisits(vector<string>& cpdomains) {
        unordered_map<string, int> freq;
        for (const string& item : cpdomains) {
            int sp = item.find(' ');
            int count = stoi(item.substr(0, sp));
            string domain = item.substr(sp + 1);

            vector<string> labels;
            string cur;
            stringstream ss(domain);
            while (getline(ss, cur, '.')) labels.push_back(cur);

            string suffix;
            for (int i = (int)labels.size() - 1; i >= 0; --i) {
                suffix = labels[i] + (suffix.empty() ? "" : "." + suffix);
                freq[suffix] += count;
            }
        }

        vector<string> ans;
        ans.reserve(freq.size());
        for (auto& [d, c] : freq) {
            ans.push_back(to_string(c) + " " + d);
        }
        return ans;
    }
};
class Solution:
    def subdomainVisits(self, cpdomains: List[str]) -> List[str]:
        freq = defaultdict(int)
        for item in cpdomains:
            cnt_str, domain = item.split()
            cnt = int(cnt_str)
            labels = domain.split('.')
            suffix = ''
            for i in range(len(labels) - 1, -1, -1):
                suffix = labels[i] if not suffix else labels[i] + '.' + suffix
                freq[suffix] += cnt
        return [f"{c} {d}" for d, c in freq.items()]
var subdomainVisits = function(cpdomains) {
  const freq = new Map();

  for (const item of cpdomains) {
    const [cntStr, domain] = item.split(' ');
    const cnt = Number(cntStr);
    const labels = domain.split('.');
    let suffix = '';

    for (let i = labels.length - 1; i >= 0; i--) {
      suffix = suffix === '' ? labels[i] : `${labels[i]}.${suffix}`;
      freq.set(suffix, (freq.get(suffix) || 0) + cnt);
    }
  }

  const ans = [];
  for (const [d, c] of freq.entries()) {
    ans.push(`${c} ${d}`);
  }
  return ans;
};

中文

题目概述

输入数组 cpdomains 中每个元素格式为 "count domain"。访问次数不仅属于完整域名,也属于它的所有后缀子域名,要求输出每个子域名的总访问次数。

核心思路

例如 discuss.leetcode.com 的一次访问,同时贡献给 discuss.leetcode.comleetcode.comcom。因此每条输入只需解析一次 count,再把它累加到所有后缀键上。

算法步骤

- 用哈希表 freq 统计子域名总次数。
- 逐条解析出 countdomain
- 将域名按 . 分段,从右向左构造后缀。
- 每构造一个后缀就加上 count
- 最后把哈希表转成题目要求的字符串数组。

复杂度分析

时间复杂度:O(n * k)k 为域名平均段数。
空间复杂度:O(m)m 为不同子域名数量。

常见陷阱

- 漏统计顶级域名(如 com)。
- 按空格分割时处理不当。
- 后缀拼接顺序写反。

多语言参考实现(Java / Go / C++ / Python / JavaScript)

class Solution {
    public List<String> subdomainVisits(String[] cpdomains) {
        Map<String, Integer> freq = new HashMap<>();
        for (String item : cpdomains) {
            String[] parts = item.split(" ");
            int count = Integer.parseInt(parts[0]);
            String[] labels = parts[1].split("\\.");
            String suffix = "";
            for (int i = labels.length - 1; i >= 0; i--) {
                suffix = labels[i] + (suffix.isEmpty() ? "" : "." + suffix);
                freq.put(suffix, freq.getOrDefault(suffix, 0) + count);
            }
        }
        List<String> ans = new ArrayList<>();
        for (Map.Entry<String, Integer> e : freq.entrySet()) {
            ans.add(e.getValue() + " " + e.getKey());
        }
        return ans;
    }
}
func subdomainVisits(cpdomains []string) []string {
    freq := map[string]int{}
    for _, item := range cpdomains {
        parts := strings.SplitN(item, " ", 2)
        count, _ := strconv.Atoi(parts[0])
        labels := strings.Split(parts[1], ".")
        suffix := ""
        for i := len(labels) - 1; i >= 0; i-- {
            if suffix == "" {
                suffix = labels[i]
            } else {
                suffix = labels[i] + "." + suffix
            }
            freq[suffix] += count
        }
    }
    ans := make([]string, 0, len(freq))
    for d, c := range freq {
        ans = append(ans, fmt.Sprintf("%d %s", c, d))
    }
    return ans
}
class Solution {
public:
    vector<string> subdomainVisits(vector<string>& cpdomains) {
        unordered_map<string, int> freq;
        for (const string& item : cpdomains) {
            int sp = item.find(' ');
            int count = stoi(item.substr(0, sp));
            string domain = item.substr(sp + 1);

            vector<string> labels;
            string cur;
            stringstream ss(domain);
            while (getline(ss, cur, '.')) labels.push_back(cur);

            string suffix;
            for (int i = (int)labels.size() - 1; i >= 0; --i) {
                suffix = labels[i] + (suffix.empty() ? "" : "." + suffix);
                freq[suffix] += count;
            }
        }

        vector<string> ans;
        ans.reserve(freq.size());
        for (auto& [d, c] : freq) {
            ans.push_back(to_string(c) + " " + d);
        }
        return ans;
    }
};
class Solution:
    def subdomainVisits(self, cpdomains: List[str]) -> List[str]:
        freq = defaultdict(int)
        for item in cpdomains:
            cnt_str, domain = item.split()
            cnt = int(cnt_str)
            labels = domain.split('.')
            suffix = ''
            for i in range(len(labels) - 1, -1, -1):
                suffix = labels[i] if not suffix else labels[i] + '.' + suffix
                freq[suffix] += cnt
        return [f"{c} {d}" for d, c in freq.items()]
var subdomainVisits = function(cpdomains) {
  const freq = new Map();

  for (const item of cpdomains) {
    const [cntStr, domain] = item.split(' ');
    const cnt = Number(cntStr);
    const labels = domain.split('.');
    let suffix = '';

    for (let i = labels.length - 1; i >= 0; i--) {
      suffix = suffix === '' ? labels[i] : `${labels[i]}.${suffix}`;
      freq.set(suffix, (freq.get(suffix) || 0) + cnt);
    }
  }

  const ans = [];
  for (const [d, c] of freq.entries()) {
    ans.push(`${c} ${d}`);
  }
  return ans;
};

Comments