Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

722. Remove Comments - Leetcode Solution

Code Implementation

class Solution:
    def removeComments(self, source):
        ans = []
        in_block = False
        new_line = []
        for line in source:
            i = 0
            n = len(line)
            if not in_block:
                new_line = []
            while i < n:
                if not in_block and i + 1 < n and line[i] == '/' and line[i+1] == '/':
                    break  # Ignore rest of the line
                elif not in_block and i + 1 < n and line[i] == '/' and line[i+1] == '*':
                    in_block = True
                    i += 1  # Skip the '*'
                elif in_block and i + 1 < n and line[i] == '*' and line[i+1] == '/':
                    in_block = False
                    i += 1  # Skip the '/'
                elif not in_block:
                    new_line.append(line[i])
                i += 1
            if not in_block and new_line:
                ans.append(''.join(new_line))
        return ans
      
class Solution {
public:
    vector<string> removeComments(vector<string>& source) {
        vector<string> ans;
        bool in_block = false;
        string newline;
        for (auto& line : source) {
            int i = 0, n = line.size();
            if (!in_block) newline = "";
            while (i < n) {
                if (!in_block && i+1 < n && line[i] == '/' && line[i+1] == '/') {
                    break;
                } else if (!in_block && i+1 < n && line[i] == '/' && line[i+1] == '*') {
                    in_block = true;
                    ++i;
                } else if (in_block && i+1 < n && line[i] == '*' && line[i+1] == '/') {
                    in_block = false;
                    ++i;
                } else if (!in_block) {
                    newline += line[i];
                }
                ++i;
            }
            if (!in_block && !newline.empty()) {
                ans.push_back(newline);
            }
        }
        return ans;
    }
};
      
class Solution {
    public List<String> removeComments(String[] source) {
        List<String> ans = new ArrayList<>();
        boolean inBlock = false;
        StringBuilder newline = new StringBuilder();
        for (String line : source) {
            int i = 0, n = line.length();
            if (!inBlock) newline.setLength(0);
            while (i < n) {
                if (!inBlock && i+1 < n && line.charAt(i) == '/' && line.charAt(i+1) == '/') {
                    break;
                } else if (!inBlock && i+1 < n && line.charAt(i) == '/' && line.charAt(i+1) == '*') {
                    inBlock = true;
                    i++;
                } else if (inBlock && i+1 < n && line.charAt(i) == '*' && line.charAt(i+1) == '/') {
                    inBlock = false;
                    i++;
                } else if (!inBlock) {
                    newline.append(line.charAt(i));
                }
                i++;
            }
            if (!inBlock && newline.length() > 0) {
                ans.add(newline.toString());
            }
        }
        return ans;
    }
}
      
var removeComments = function(source) {
    let ans = [];
    let inBlock = false;
    let newline = [];
    for (let line of source) {
        let i = 0, n = line.length;
        if (!inBlock) newline = [];
        while (i < n) {
            if (!inBlock && i+1 < n && line[i] === '/' && line[i+1] === '/') {
                break;
            } else if (!inBlock && i+1 < n && line[i] === '/' && line[i+1] === '*') {
                inBlock = true;
                i++;
            } else if (inBlock && i+1 < n && line[i] === '*' && line[i+1] === '/') {
                inBlock = false;
                i++;
            } else if (!inBlock) {
                newline.push(line[i]);
            }
            i++;
        }
        if (!inBlock && newline.length > 0) {
            ans.push(newline.join(''));
        }
    }
    return ans;
};
      

Problem Description

You are given a list of strings source, where each string represents a line of source code. The code may contain comments in two styles:

  • Line comments: start with // and extend to the end of the line.
  • Block comments: start with /* and end with */. Block comments can span multiple lines.
Your task is to remove all comments from the code and return the code as a list of strings, with each string representing a line of code with comments removed. Blank lines (lines that become empty after removing comments) should be omitted from the result.

Constraints:
  • Each line in source is non-empty.
  • Block comments are properly closed and do not nest.
  • There is only one valid way to remove the comments.

Thought Process

At first glance, the problem seems to require searching for // and /* ... */ patterns and removing them. A brute-force solution might involve replacing these patterns line by line, but block comments can span multiple lines, making the task more complex. We need to keep track of whether we are inside a block comment and ignore everything until the block comment ends. For line comments, everything after // on the same line can be ignored.

The challenge is to process each line while remembering if we are inside a block comment, and to build the resulting lines only from code outside of comments. We must also handle cases where comments start and end within the same line, or where a block comment starts on one line and ends on another.

Solution Approach

The solution involves iterating through each character of each line, carefully managing the state of whether we are inside a block comment. Here’s a step-by-step approach:

  1. Initialize a flag (e.g., in_block) to track whether we are inside a block comment.
  2. Iterate over each line in source.
  3. For each character in the line:
    • If not in a block comment and encounter //, ignore the rest of the line.
    • If not in a block comment and encounter /*, set in_block to true and skip the /*.
    • If in a block comment and encounter */, set in_block to false and skip the */.
    • If not in a block comment, add the character to a buffer for the new line.
  4. After processing a line, if not in a block comment and the buffer is not empty, add the buffer as a new line to the result.
  5. Repeat until all lines are processed.

This approach ensures that both line and block comments are correctly removed, and only non-empty lines of code are returned.

Example Walkthrough

Input:

    source = [
      "int main() {",
      "  // initialize",
      "  int x = 1; /* start block",
      "  more block",
      "  end block */ int y = 2;",
      "  return 0;",
      "}"
    ]
    
Step-by-step:
  • Line 1: int main() { — No comments, added to result.
  • Line 2: // initialize — Line comment, whole line ignored.
  • Line 3: int x = 1; /* start block — Code before /* kept: int x = 1; . Set in_block = True.
  • Line 4: more block — Inside block comment, ignored.
  • Line 5: end block */ int y = 2; — Block comment ends at */, rest of line ( int y = 2;) kept.
  • Line 6: return 0; — No comments, added to result.
  • Line 7: } — No comments, added to result.
Output:
    [
      "int main() {",
      "  int x = 1; ",
      " int y = 2;",
      "  return 0;",
      "}"
    ]
    
Each step shows how the code is built, skipping or trimming lines as needed.

Time and Space Complexity

Brute-force approach: If you tried to repeatedly search and remove comment patterns, it would still visit each character at least once, so the time complexity would be O(N), where N is the total number of characters in all lines.

Optimized approach (as above):

  • Time Complexity: O(N) — Each character in the source is visited once.
  • Space Complexity: O(N) — The output can be as large as the input if there are no comments.
The solution is efficient because it performs a single pass through the input and uses a buffer for each output line.

Summary

The Remove Comments problem is elegantly solved by tracking whether you are inside a block comment and building output lines character by character. The key insight is to handle block and line comments differently, and to carefully manage state as you process each character. The solution is efficient, requiring only a single pass through the source code, and is robust to all edge cases described by the problem constraints.