给你一个只包含三种字符的字符串,支持的字符类型分别是 ‘(’、‘)’ 和 ‘*’。请你检验这个字符串是否为有效字符串,如果是有效字符串返回true 。
有效字符串符合如下规则:
任何左括号 ‘(’ 必须有相应的右括号 ‘)’。 任何右括号 ‘)’ 必须有相应的左括号 ‘(’ 。 左括号 ‘(’ 必须在对应的右括号之前 ‘)’。 ‘*’ 可以被视为单个右括号 ‘)’ ,或单个左括号 ‘(’ ,或一个空字符串。 一个空字符串也被视为有效字符串。
示例 1:
输入:s = “()”
输出:true
示例 2:
输入:s = “(*)”
输出:true
示例 3:
输入:s = “(*))”
输出:true
提示:
1 <= s.length <= 100
s[i] 为 ‘(’、‘)’ 或 ‘*’
解题思路:
1、最开始我的想法是利用单括号思维解决本题‘(’无非多了一个‘*’,谁缺给谁补,事实证明我的想法还是很有漏洞
错误代码:
class Solution {
public int balance = 0, middle = 0;
public boolean checkValidString(String s) {
for(char a : s.toCharArray()) {
if(a == '(') {
if(check()) return false;
balance ++;
}
else if(a == ')') {
balance --;
if(check()) return false;
}
else if(a == '*') middle ++;
}
return balance == 0;
}
public boolean check() {
while(balance < 0 && middle > 0) {
balance ++;
middle --;
}
return balance < 0;
}
}
上述代码有巨大漏洞即无法判别**((
和 **))
的对错,也就是说我没有考虑到*
符号的位置的重要性
解决策略:记录下标和栈的引入
2、动态规划去一步一步分析,是可行的,因为其考虑到了‘*’在不同情况产生的不同结果
代码:
class Solution {
public boolean checkValidString(String s) {
int n = s.length();
boolean[][] f = new boolean[n + 1][n + 1];
f[0][0] = true;
for (int i = 1; i <= n; i++) {
char c = s.charAt(i - 1);
for (int j = 0; j <= i; j++) {
if (c == '(') {
if (j - 1 >= 0) f[i][j] = f[i - 1][j - 1];
} else if (c == ')') {
if (j + 1 <= i) f[i][j] = f[i - 1][j + 1];
} else {
f[i][j] = f[i - 1][j];
if (j - 1 >= 0) f[i][j] |= f[i - 1][j - 1];
if (j + 1 <= i) f[i][j] |= f[i - 1][j + 1];
}
}
}
return f[n][0];
}
}
3、下述模拟也可以避免符号位置考虑不到的错误
代码:
class Solution {
public boolean checkValidString(String s) {
// l: 左括号最少可能有多少个
// r: 左括号最多可能有多少个
int l = 0, r = 0;
for (char c : s.toCharArray()) {
// 遇到'('所有可能性加一
// 遇到')'所有可能性减一
// 遇到'*',最少的可能性可以变少,最多的可能性也同样可以变多,这取决于这个星号最终我们看成什么,但是可能性都在
if (c == '(') {
l++; r++;
} else if (c == ')') {
l--; r--;
} else {
l--; r++;
}
// 当前左括号最少个数不能为负
l = Math.max(l, 0);
// 这种情况其实发生在r本身是负数的时候,也就是我们常见的右括号太多了
if (l > r) return false;
}
// 能取到0个左括号才是满足平衡的
return l == 0;
}
}