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;
};
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:
//
and extend to the end of the line./*
and end with */
. Block comments can span multiple lines.source
is non-empty.
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.
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:
in_block
) to track whether we are inside a block comment.source
.//
, ignore the rest of the line./*
, set in_block
to true and skip the /*
.*/
, set in_block
to false and skip the */
.This approach ensures that both line and block comments are correctly removed, and only non-empty lines of code are returned.
Input:
source = [ "int main() {", " // initialize", " int x = 1; /* start block", " more block", " end block */ int y = 2;", " return 0;", "}" ]Step-by-step:
int main() {
— No comments, added to result.// initialize
— Line comment, whole line ignored.int x = 1; /* start block
— Code before /*
kept: int x = 1;
. Set in_block = True
.more block
— Inside block comment, ignored.end block */ int y = 2;
— Block comment ends at */
, rest of line ( int y = 2;
) kept.return 0;
— No comments, added to result.}
— No comments, added to result.[ "int main() {", " int x = 1; ", " int y = 2;", " return 0;", "}" ]Each step shows how the code is built, skipping or trimming lines as needed.
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):
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.