题干
给你一个包含若干星号 * 的字符串 s 。
在一步操作中,你可以:
选中 s 中的一个星号。
移除星号 左侧 最近的那个 非星号 字符,并移除该星号自身。
返回移除 所有 星号之后的字符串。
注意:
生成的输入保证总是可以执行题面中描述的操作。
可以证明结果字符串是唯一的。
示例 1:
输入:s = “leet**cod*e”
输出:“lecoe”
解释:从左到右执行移除操作:
- 距离第 1 个星号最近的字符是 “leet**code" 中的 ‘t’ ,s 变为 "leecod*e” 。
- 距离第 2 个星号最近的字符是 “leecode” 中的 ‘e’ ,s 变为 “lecod*e” 。
- 距离第 3 个星号最近的字符是 “lecod*e” 中的 ‘d’ ,s 变为 “lecoe” 。
不存在其他星号,返回 “lecoe” 。
示例 2:
输入:s = “erase*****”
输出:“”
解释:整个字符串都会被移除,所以返回空字符串。
题解
这里星号要抹去前面的文字,可以想到用栈,先入栈,当出现星号的时候弹出字符。
然后有了初始的版本
为什么这里没有用stack,因为最后append拼接的时候感觉LinkedList写法更加方便
public String removeStars(String s) {
char[] charArray = s.toCharArray();
LinkedList<Character> list = new LinkedList<>();
for (int i = 0; i < charArray.length; i++) {
if (charArray[i] == '*') {
if (!list.isEmpty()) {
list.pollLast();
}
} else {
list.addLast(charArray[i]);
}
}
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
stringBuilder.append(list.get(i));
}
return stringBuilder.toString();
}
然后时间没有过,感觉是因为LinkedList节点上不连续的原因
然后换了栈的方式,栈在java底层的实现是数组,有自动扩容的功能。
只所以这样写,还是因为不想最后拼接字符串的时候重复两次
public String removeStars(String s) {
// 指针来回跳吧
char[] charArray = s.toCharArray();
Stack<Character> stack = new Stack<>();
int count = 0;
for (int i = charArray.length - 1; i >= 0; i--) {
if (charArray[i] == '*') {
count++;
} else if (count > 0) {
count--;
} else {
stack.add(charArray[i]);
}
}
StringBuilder stringBuilder = new StringBuilder();
while (!stack.isEmpty()) {
stringBuilder.append(stack.pop());
}
return stringBuilder.toString();
}
但是跑出来的成绩还是不理想
这里官方的题解用了stringBuilder的setLength方法,还是第一次接触, 内部调用 Arrays.fill(value, count, newLength, ‘\0’);
public String removeStars(String s) {
StringBuilder res = new StringBuilder();
for (char c : s.toCharArray()) {
if (c != '*') {
res.append(c);
} else {
res.setLength(res.length() - 1); // 内部调用 Arrays.fill(value, count, newLength, '\0');
}
}
return res.toString(); // 内部调用 return new String(value, 0, count);
}
题外话:
Arrays.fill(array, startIndex, endIndex, value); 填充到指定范围位置
- array:这是目标数组,您想要在其中填充元素。数组的元素类型必须与value的类型相匹配。
- startIndex:这是要填充范围的起始索引,包括在内。这是填充操作的起始位置。
- endIndex:这是要填充范围的结束索引,不包括在内。填充操作将一直持续到索引endIndex - 1。
- value:这是要设置的值,它必须与数组元素的类型相匹配。在指定范围内的所有元素都将被设置为这个值。
res.setLength(res.length() - 1); 这段代码的意思就是把数组的最后的位置的空格给设置了空
进一步优化:
这里不用stringBuilder,我们可以自己用数组来维护,因为stringbuilder的底层就是数组
然后可以减少一次遍历来拼接字符串
public static String removeStars(String s) {
int[] arr = new int[s.length()];
int count = 0;
for (char c : s.toCharArray()) {
if (c != '*') {
arr[count] = c;
count++;
} else if (count > 0) {
count--;
}
}
return new String(arr, 0, count);
}
从38到了95
再进一步优化:
上面有两个数组,然后希望用一个数组实现,于是代码演化如下
public static String removeStars(String s) {
int count = 0;
char[] charArray = s.toCharArray();
for (int i = 0; i < charArray.length; i++) {
if (charArray[i] != '*') {
charArray[count] = charArray[i];
count++;
} else if (count > 0) {
count--;
}
}
return new String(charArray, 0, count);
}
总结
官方的题解是很妙,但是还是要回归朴素的思维一步一步推到为妙