Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

830. Positions of Large Groups - Leetcode Solution

Problem Description

The "Positions of Large Groups" problem asks you to find all the intervals in a string where a group of the same consecutive character appears at least three times in a row. Specifically, you are given a string s consisting of lowercase letters. Your task is to identify every contiguous segment (or group) of the same character that is at least length 3, and return a list of intervals [start, end] (inclusive) indicating the start and end indices of each such group.

  • Each group must be at least three characters long.
  • Return the intervals in increasing order of start index.
  • Each interval is a pair of indices [start, end] (inclusive).
  • There is no need to merge overlapping groups, as groups are always of a single character.

Example: For s = "abbxxxxzzy", the output should be [[3, 6]] because the substring "xxxx" (indices 3-6) is the only group of length at least 3.

Thought Process

To solve this problem, we need to examine the string and find stretches where the same character repeats at least three times. The most straightforward approach is to scan the string from left to right, keeping track of the start and end of each group of identical characters. When a group ends (i.e., the next character is different or we've reached the end of the string), we check if its length is 3 or more. If so, we record its start and end indices.

Initially, one might consider brute-forcing all possible substrings and checking if they meet the criteria, but this would be highly inefficient. Instead, we can optimize by only tracking contiguous groups, which can be efficiently done in a single pass.

The key insight is to use two pointers: one to mark the start of a group, and one to iterate through the string. By comparing the current character to the previous one, we can detect when a group ends and process it accordingly.

Solution Approach

Here is a step-by-step breakdown of the algorithm:

  1. Initialize: Create an empty list to store the result intervals. Set a variable start to 0, indicating the start of the current group.
  2. Iterate: Loop through the string using an index i from 0 to len(s) (inclusive; we go one past the end for easier processing).
  3. Check for group end: At each step, check if we've reached the end of the string or if the current character is different from the previous one. If so:
    • Calculate the length of the current group as i - start.
    • If the group length is at least 3, append [start, i-1] to the result list.
    • Update start to the current index i (to start tracking a new group).
  4. Return the result: After the loop, return the list of intervals.

This approach is efficient because it only requires a single pass through the string and uses constant additional space (aside from the output).

Example Walkthrough

Let's use the example s = "abbxxxxzzy":

  • Step 1: Start at index 0 ('a'), start = 0.
  • Step 2: Index 1 ('b') is different from index 0, so group 'a' is of length 1. Not large, move start to 1.
  • Step 3: Index 2 ('b') is same as index 1, continue.
  • Step 4: Index 3 ('x') is different from index 2, group 'bb' is length 2. Not large, move start to 3.
  • Step 5: Index 4,5,6 ('x') are the same as previous, continue.
  • Step 6: Index 7 ('z') is different from index 6, group 'xxxx' (indices 3-6) is length 4. This is a large group, so we add [3,6] to the result.
  • Step 7: Continue with the rest of the string, but no other group reaches length 3.
  • Final Output: [[3,6]]

Time and Space Complexity

Brute-force approach: Checking every possible substring would be O(N2), since there are O(N2) substrings and each check might take O(1) time.

Optimized approach (current solution):

  • Time Complexity: O(N), where N is the length of the string. We make a single pass through the string.
  • Space Complexity: O(1) extra space, not counting the output. The output list could be up to O(N) in the worst case (e.g., if the whole string is a single large group).

Summary

The "Positions of Large Groups" problem is efficiently solved by scanning the string once and tracking the start and end of each group of identical characters. By checking the length of each group as it ends, we can quickly identify all large groups and record their intervals. This approach is both simple and optimal, requiring only a single pass and minimal extra memory.

Code Implementation

class Solution:
    def largeGroupPositions(self, s: str):
        res = []
        n = len(s)
        start = 0
        for i in range(n + 1):
            if i == n or s[i] != s[start]:
                if i - start >= 3:
                    res.append([start, i - 1])
                start = i
        return res
      
class Solution {
public:
    vector<vector<int>> largeGroupPositions(string s) {
        vector<vector<int>> res;
        int n = s.size();
        int start = 0;
        for (int i = 0; i <= n; ++i) {
            if (i == n || s[i] != s[start]) {
                if (i - start >= 3) {
                    res.push_back({start, i - 1});
                }
                start = i;
            }
        }
        return res;
    }
};
      
class Solution {
    public List<List<Integer>> largeGroupPositions(String s) {
        List<List<Integer>> res = new ArrayList<>();
        int n = s.length();
        int start = 0;
        for (int i = 0; i <= n; i++) {
            if (i == n || s.charAt(i) != s.charAt(start)) {
                if (i - start >= 3) {
                    res.add(Arrays.asList(start, i - 1));
                }
                start = i;
            }
        }
        return res;
    }
}
      
var largeGroupPositions = function(s) {
    let res = [];
    let n = s.length;
    let start = 0;
    for (let i = 0; i <= n; i++) {
        if (i === n || s[i] !== s[start]) {
            if (i - start >= 3) {
                res.push([start, i - 1]);
            }
            start = i;
        }
    }
    return res;
};