package com.freetymekiyan.algorithms.level.hard;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
/**
* Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible
* results.
* <p>
* Note: The input string may contain letters other than the parentheses ( and ).
* <p>
* Examples:
* "()())()" -> ["()()()", "(())()"]
* "(a)())()" -> ["(a)()()", "(a())()"]
* ")(" -> [""]
* <p>
* Company Tags: Facebook
* Tags: Depth-first Search, Breadth-first Search
* Similar Problems: (E) Valid Parentheses
*/
public class RemoveInvalidParentheses {
/**
* BFS.
* Generate all possible next strings by removing one paren from current string.
* First add the original string to a queue and a set to start BFS.
* While queue is not empty:
* | Poll a string from the queue.
* | Check if the polled string is valid, if its valid:
* | Set the found flag. We don't need to generate next level.
* | If found is true:
* | Continue to the next string in queue.
* | If found is false:
* | Generate all possible strings for the next level by removing one parentheses.
* <p>
* Note that once we found one valid string, we know the minimum number.
* No need to generate the next possible strings anymore.
* Use a String set to avoid BFS cycles or duplicate checks.
*/
public List<String> removeInvalidParentheses(String s) {
if (s == null) { // "" should return [""].
return Collections.emptyList();
}
List<String> res = new ArrayList<>();
Set<String> visited = new HashSet<>(); // Store visited strings.
Queue<String> queue = new LinkedList<>();
queue.add(s);
visited.add(s);
boolean found = false; // Flag to stop generating new strings.
while (!queue.isEmpty()) {
String cur = queue.poll();
// Visit.
if (isValid(cur)) { // First valid string is found.
res.add(cur);
found = true;
}
if (found) { // No need to generate the more strings.
continue; // Still check all the strings remain in queue.
}
// Generate all possible strings by removing one paren.
for (int i = 0; i < cur.length(); i++) {
if (cur.charAt(i) != '(' && cur.charAt(i) != ')') { // Skip chars that are not paren.
continue;
}
cur = cur.substring(0, i) + cur.substring(i + 1); // Remove i.
if (!visited.contains(cur)) {
queue.add(cur);
visited.add(cur);
}
}
}
return res;
}
/**
* Use an integer counter as a Stack.
* Make sure there are more left parens than right ones.
* Increase the counter when there is a left paren.
* If there is a right paren, check whether there is enough paren to match first.
* If counter is zero, which means no left paren available, return false;
* Otherwise decrement counter by 1.
* Return true if counter is 0 that is fully matched.
* Otherwise false.
*/
private boolean isValid(String s) {
int count = 0;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(') {
count++;
} else if (c == ')' && count-- == 0) {
return false;
}
}
return count == 0;
}
/**
* DFS.
* https://leetcode.com/discuss/81478/easy-short-concise-and-fast-java-dfs-3-ms-solution
*/
public List<String> removeInvalidParentheses2(String s) {
List<String> res = new ArrayList<>();
remove(s, res, 0, 0, new char[]{'(', ')'});
return res;
}
private void remove(String s, List<String> res, int last_i, int last_j, char[] par) {
for (int counter = 0, i = last_i; i < s.length(); i++) {
if (s.charAt(i) == par[0]) {
counter++;
}
if (s.charAt(i) == par[1]) {
counter--;
}
if (counter >= 0) {
continue;
}
for (int j = last_j; j <= i; j++) {
if (s.charAt(j) == par[1] && (j == last_j || s.charAt(j - 1) != par[1])) {
remove(s.substring(0, j) + s.substring(j + 1, s.length()), res, i, j, par);
}
}
return;
}
String reversed = new StringBuilder(s).reverse().toString();
if (par[0] == '(') { // finished left to right
remove(reversed, res, 0, 0, new char[]{')', '('});
} else { // finished right to left
res.add(reversed);
}
}
}