AlgoMaster Logo

Group Shifted Strings

mediumFrequencyUpdated September 21, 2026

Understanding the Problem

The core idea behind "shifting" is that every letter in the string moves forward by the same amount. "abc" shifted by 1 becomes "bcd", shifted by 2 becomes "cde", and so on. The shift wraps around, so "xyz" shifted by 1 becomes "yza", and eventually you get back to "abc" after 26 shifts.

Two strings belong to the same shifting sequence if they have the same length and the same pattern of differences between consecutive characters. For example, "abc" has differences [1, 1] (b-a=1, c-b=1), and "xyz" also has differences [1, 1] (y-x=1, z-y=1). So they're in the same group.

The wrap-around needs care. "az" has a difference of 25 (z-a=25), and "ba" also has a difference of 25 (a-b = -1, but mod 26 = 25). So "az" and "ba" are in the same group.

This gives a way to group: compute the differences between consecutive characters (mod 26) for each string, and strings with identical difference sequences belong to the same group.

Key Constraints:

  • 1 <= strings.length <= 200 → The input is small enough that even an O(n^2) pairwise comparison runs comfortably, so correctness, not raw speed, drives the choice of approach.
  • 1 <= strings[i].length <= 50 → Strings are short, so computing a difference key per string is cheap.
  • strings[i] consists of lowercase English letters → Only 26 characters, so differences fall in the range [0, 25] and a single mod 26 handles every wrap-around.

Approach 1: Brute Force (Pairwise Comparison)

Intuition

For every pair of strings, check whether they belong to the same shifting sequence. Two strings are in the same group if they have the same length and one can be shifted to match the other. To check this, compute the shift amount from the first character and verify the same shift holds at every position.

Pick a string, compare it against every other ungrouped string, and collect the matches into one group.

Algorithm

  1. Initialize a visited array to track which strings have already been grouped.
  2. For each unvisited string, start a new group.
  3. Compare it against every other unvisited string.
  4. Two strings match if they have the same length and the shift from the first character of one to the first character of the other is consistent across all positions (with mod 26 for wrap-around).
  5. Add matching strings to the current group and mark them as visited.
  6. Return all groups.

Visualization and Code

Loading animation...

The pairwise comparison repeats work: each string is compared against many others, and the comparison itself recomputes shifts from scratch. The next approach replaces the comparison with a single key computed per string, so strings that share a key fall into the same group without ever being compared directly.

Approach 2: Hash Map with Difference Key (Optimal)

Intuition

Instead of comparing every pair, assign each string a signature that identifies which shift group it belongs to. Two strings with the same signature go into the same group.

The shift pattern of a string is fixed by the differences between consecutive characters. "abc" has differences [1, 1], "bcd" also has differences [1, 1], and "xyz" also has differences [1, 1]. But "acef" has differences [2, 2, 1], a different pattern.

For each string, compute its difference sequence (mod 26 to handle wrap-around), use that sequence as a hash map key, and group strings by key. A single-character string has no consecutive pairs, so its difference sequence is empty, and all single-character strings share that empty key.

Algorithm

  1. Create a hash map where keys are difference sequences and values are lists of strings.
  2. For each string, compute its difference key: for each pair of consecutive characters, calculate (s[i+1] - s[i] + 26) % 26.
  3. Convert the difference sequence to a string (e.g., "1,1" for "abc") to use as the hash map key.
  4. Add the string to the list associated with its key.
  5. Return all the lists from the hash map.

Visualization and Code

Loading animation...

This is optimal: O(n * k) time is the lower bound, since every character of every string has to be read at least once to determine its group.