Valid Parenthesis String
Given a string containing only three types of characters: '(', ')' and '*', write a function to check whether this string is valid. We define the validity of a string by these rules:
- Any left parenthesis
'('
must have a corresponding right parenthesis')'
. - Any right parenthesis
')'
must have a corresponding left parenthesis'('
. - Left parenthesis
'('
must go before the corresponding right parenthesis')'
. '*'
could be treated as a single right parenthesis')'
or a single left parenthesis'('
or an empty string.- An empty string is also valid.
Example 1:
Input: "()"
Output: True
Example 2:
Input: "(*)"
Output: True
Example 3:
Input: "(*))"
Output: True
Thoughts:
- DP
- "two counter": min_op_left: how many open "(" if regarding all * to be ")"; max_op_left: how many open "(" if regarding all * to be "("
- key idea: max_op_left should always be >= 0 in order to be considered legal, otherwise it will have unbalanced ")" and the parenthesis will never be balanced by further appending any char after it.
Code: unordered DP with memoization Time: O(n^3) Space: O(n^2), Top-Down
class Solution {
public:
bool checkValidString(string s) {
int n = s.length();
f = vector<vector<int>> (n, vector<int>(n, -1));
return isValid(s, 0, n - 1);
}
private:
vector<vector<int>> f;
// method checking substring s[start,...end] is valid
bool isValid(const string &s, int start, int end){
if(start > end) return true;
if(start == end) return f[start][end] = (s[start] == '*');
if(f[start][end]>= 0) return f[start][end]; // is already memorized
if((s[start] == '(' || s[start] == '*')
&& (s[end] == ')'|| s[end] == '*')
&& isValid(s, start + 1, end - 1))
return f[start][end] = 1;
for(int i = start; i < end; i++){
if(isValid(s, start, i) && isValid(s, i + 1, end))
return f[start][end] = 1;
}
return f[start][end] = 0;
}
};
Code: Ordered DP with memoization Time: O(n^3) Space: O(n^2), Bottom Up
class Solution {
public:
bool checkValidString(const string& s) {
int l = s.length();
if (l == 0) return true;
vector<vector<int>> dp(l, vector<int>(l, 0));
for (int i = 0; i < l; ++i)
if (s[i] == '*') dp[i][i] = 1;
for (int len = 2; len <= l; ++len)
for (int i = 0; i <= l - len; ++i) {
int j = i + len - 1;
// (...), *...), (...*, *...*
if ((s[i] == '(' || s[i] == '*')
&&(s[j] == ')' || s[j] == '*'))
if (len == 2 || dp[i + 1][j - 1]) {
dp[i][j] = 1;
continue;
}
for (int k = i; k < j; ++k)
if (dp[i][k] && dp[k + 1][j]) {
dp[i][j] = 1;
break;
}
}
return dp[0][l - 1];
}
};
Code Counting Time: O(n), Space: O(1)
class Solution {
public:
bool checkValidString(string s) {
int max_open_left = 0, min_open_left = 0;
for(char c: s){
if(c == '(') min_open_left++; else min_open_left--;
if(c != ')') max_open_left++; else max_open_left--;
if(max_open_left < 0) return false;
min_open_left = max(0, min_open_left); // we cannot use previous "*" to fullfill later '(": reset the min_open_left
}
return min_open_left == 0;
}
};
class Solution {
public:
bool checkValidString(string s) {
int max_open_left = 0, min_open_left = 0;
for(char c: s){
if(c == '(') min_open_left++; else if(min_open_left > 0) min_open_left--;
if(c != ')') max_open_left++;
else if (max_open_left == 0) return false;
else max_open_left--;
}
return min_open_left == 0;
}
};
Python
class Solution(object):
def checkValidString(self, s):
"""
:type s: str
:rtype: bool
"""
max_open, min_open = 0, 0
for c in s:
# if c == '(': min_open += 1
# else: min_open -=1
min_open = min_open + 1 if c == '(' else min_open - 1
max_open = max_open + 1 if c != ')' else max_open - 1
if max_open < 0: return False
min_open = max(0, min_open)
return min_open == 0
Special Thanks to Huahua's blog