Quick Overview

Partition a list of lowercase words into groups whose members contain exactly the same letters the same number of times, keeping words and groups in input order. Tests hashing-based grouping, handling of duplicates and empty strings, and analysis of time and space complexity for many short strings.

Partition Words Into Groups With Identical Letter Counts

Company: Workday

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Technical Screen

Given a list of lowercase words, partition them into groups so that two words are in the same group exactly when one can be turned into the other by reordering its letters, that is, when both contain every letter the same number of times. In the interview this was discussed verbally, with the time and space complexity of the approach; here it is a function to implement. ### Function Signature ```python def group_by_letters(words: list[str]) -> list[list[str]]: ``` ### Rules - Two words belong to the same group if and only if, for every letter from `a` to `z`, they contain that letter the same number of times. Empty strings form one group together. - Every element of `words` appears in exactly one group, once per occurrence: duplicate words stay as separate entries of the same group. - Within a group, words appear in the order in which they appear in `words`. - Groups are ordered by the position in `words` of their first word. ### Constraints - `1 <= len(words) <= 10^4` - `0 <= len(words[i]) <= 100` - Every word consists only of lowercase English letters `a` to `z`. ### Examples **Example 1** ```text Input: words = ["listen", "google", "silent", "enlist", "gogole", "cat"] Output: [["listen", "silent", "enlist"], ["google", "gogole"], ["cat"]] ``` `listen`, `silent` and `enlist` use the same six letters once each. `google` and `gogole` each contain two `g`, two `o`, one `l` and one `e`. The groups are ordered by their first words, at positions 0, 1 and 5. **Example 2** ```text Input: words = ["", "b", "", "ab", "ba", "b"] Output: [["", ""], ["b", "b"], ["ab", "ba"]] ``` Duplicates stay in their group once per occurrence, and the two empty strings form a group. The first words of the groups are at positions 0, 1 and 3. **Example 3** ```text Input: words = ["aab", "aba", "abb", "baa"] Output: [["aab", "aba", "baa"], ["abb"]] ``` `abb` uses the same two letters as `aab` but in different counts, so it forms its own group.

Overview: Partition a list of lowercase words into groups whose members contain exactly the same letters the same number of times, keeping words and groups in input order. Tests hashing-based grouping, handling of duplicates and empty strings, and analysis of time and space complexity for many short strings.

Read the full Workday Software Engineer interview experience this question came from

Given a list of lowercase words `words`, partition them into groups so that two words are in the same group exactly when one can be turned into the other by reordering its letters, that is, when both contain every letter the same number of times. Implement `group_by_letters(words)`, which returns the groups as a list of lists of words. **Rules** - Two words belong to the same group if and only if, for every letter from `a` to `z`, they contain that letter the same number of times. Empty strings form one group together. - Every element of `words` appears in exactly one group, once per occurrence: duplicate words stay as separate entries of the same group. - Within a group, words appear in the order in which they appear in `words`. - Groups are ordered by the position in `words` of their first word. These rules fix a single correct output for every input. **Constraints** - `1 <= len(words) <= 10^4` - `0 <= len(words[i]) <= 100` - Every word consists only of lowercase English letters `a` to `z` (the empty string is allowed). **Example 1** ``` Input: words = ["listen", "google", "silent", "enlist", "gogole", "cat"] Output: [["listen", "silent", "enlist"], ["google", "gogole"], ["cat"]] ``` `listen`, `silent` and `enlist` use the same six letters once each. `google` and `gogole` each contain two `g`, two `o`, one `l` and one `e`. The groups are ordered by their first words, at positions 0, 1 and 5. **Example 2** ``` Input: words = ["", "b", "", "ab", "ba", "b"] Output: [["", ""], ["b", "b"], ["ab", "ba"]] ``` Duplicates stay in their group once per occurrence, and the two empty strings form a group. The first words of the groups are at positions 0, 1 and 3.

Constraints

  • 1 <= len(words) <= 10^4
  • 0 <= len(words[i]) <= 100
  • Every word consists only of lowercase English letters a to z; the empty string is allowed.

Examples

Input: (['listen', 'google', 'silent', 'enlist', 'gogole', 'cat'],)

Expected Output: [['listen', 'silent', 'enlist'], ['google', 'gogole'], ['cat']]

Explanation: Source Example 1: groups start at positions 0, 1 and 5; google and gogole both have two g, two o, one l and one e.

Input: (['', 'b', '', 'ab', 'ba', 'b'],)

Expected Output: [['', ''], ['b', 'b'], ['ab', 'ba']]

Explanation: Source Example 2: the duplicate b is kept twice and the two empty strings form the first group; groups start at positions 0, 1 and 3.

Hints

  1. Whether two words belong together depends only on how many times each letter a to z occurs in them, not on where those letters sit.
  2. Both the order of the groups and the order inside each group follow the input order, so nothing in the output needs to be sorted.
  3. Empty strings and repeated words are valid input: every occurrence must appear exactly once in the output.

Loading coding console...

Show the approach

Approach

Algorithm: scan words from left to right. For each word build its signature, the 26 counts of the letters a to z (the empty string has the all-zero signature). A hash map sends each signature to the index of its group in the output list. If the signature is new, append a new group [word] and record its index; otherwise append the word to the recorded group.

Invariant: after the first i words have been processed, the output list holds exactly the groups of words[0..i-1], each group in input order, and the groups are ordered by the position of their first word. A group is created only when its first word is met, so it lands after every group whose first word came earlier; appending to an existing group keeps input order inside it.

Correctness: two words share a group exactly when their count vectors are equal, which is the definition in the rules. The Python reference keys on a tuple of counts; the JavaScript, Java and C++ references join the counts with a separator, so distinct count vectors (for example a=1, b=11 versus a=11, b=1) can never produce the same key.

Edge cases: a single word gives one group; all empty strings share the all-zero signature and form one group wherever they first appear; duplicate words are appended once per occurrence; words with the same set of letters but different counts (aab versus abb, ab versus aabb) get different signatures. No numeric value exceeds 32-bit range.

Time complexity:
O(S + 26n), where n = len(words) and S is the total number of characters
Space complexity:
O(S + 26n)