题目描述1
笔者解答1.1
class Solution {
public int scoreOfParentheses(String S) {
Stack<Integer> stack=new Stack<Integer>();
//-1代表左括号
for(int i=0;i<S.length();i++){
if(stack.isEmpty()||S.charAt(i)=='('){
stack.push(-1);
}else{
if(stack.peek()==-1){
stack.pop();
if(stack.isEmpty()||stack.peek()==-1)
stack.push(1);
else{
stack.push(1+stack.pop());
}
}else{
int temp=stack.pop()*2;
stack.pop();
if(stack.isEmpty()||stack.peek()==-1)
stack.push(temp);
else
stack.push(temp+stack.pop());
}
}
}
return stack.pop();
}
}
笔者分析1.2
虽然是个基础题,但写出来还是略有成就感的,重在思考过程。刚开始是打算用两个栈,一个栈用来记录括号,一个用来记录分数,但最后发现,两个栈向互受到牵制,所以必须合成一个栈,考虑到分数是整数,符号是字符型,要想合成一个栈,则可考虑将符号用特殊数字代替,显然这里的分数都是正的,所以用-1来表示左括号。遍历字符串,若为左括号则直接入栈,若为右括号,则判定栈顶是正数还是负数,若为正数,则将其扩大两倍再存入。若为负数,则弹栈,存入1,这里需要注意,存入1之前,应该先判断栈内的情况,若为正数则二者相加,若为负数则直接入栈。
题目描述2
笔者解答2.2
class Solution {
public String decodeAtIndex(String S, int K) {
int lastlength=0;
int alllength=0;
String str="";
for(int i=0;i<S.length();i++){
if(S.charAt(i)<='9'&&S.charAt(i)>='0'){
alllength=(S.charAt(i)-'0')*lastlength;
int num=S.charAt(i)-'0';
String temp_str=str;
str="";
while(num>=1){
str+=temp_str;
num--;
}
if(alllength>=K){
return ""+str.charAt((K-1)%lastlength);
}
lastlength=alllength;
}else{
lastlength++;
str=str+""+S.charAt(i);
if(lastlength>=K){
return ""+str.charAt((K-1)%lastlength);
}
}
}
return S.charAt(K-1)+"";
}
}
笔者分析2.2
这题写起来很顺利,但通过率只有1/5,刚开始我很是纳闷,直到我的代码运行超出内存限制。。。打脸了打脸了。只得求助评论区。
逆向工作法
如果我们有一个像 appleappleappleappleappleapple 这样的解码字符串和一个像 K=24 这样的索引,那么如果 K=4,答案是相同的。
一般来说,当解码的字符串等于某个长度为 size 的单词重复某些次数(例如 apple 与 size=5 组合重复6次)时,索引 K 的答案与索引 K % size 的答案相同。
我们可以通过逆向工作,跟踪解码字符串的大小来使用这种洞察力。每当解码的字符串等于某些单词 word 重复 d 次时,我们就可以将 k 减少到 K % (Word.Length)。
说实话,这思路和我一样嘛,为啥我就超出内存限制呢。主要还是str惹的祸,其实没必要每次存储字符串。不过,这逆向思维还是挺厉害的。
class Solution {
public String decodeAtIndex(String S, int K) {
long size = 0;
int N = S.length();
// Find size = length of decoded string
for (int i = 0; i < N; ++i) {
char c = S.charAt(i);
if (Character.isDigit(c))
size *= c - '0';
else
size++;
}
for (int i = N-1; i >= 0; --i) {
char c = S.charAt(i);
K %= size;
if (K == 0 && Character.isLetter(c))
return Character.toString(c);
if (Character.isDigit(c))
size /= c - '0';
else
size--;
}
throw null;
}
}
题目描述3
笔者解答3.1
class Solution {
public int sumSubarrayMins(int[] A) {
int sum=0;
for(int i=0;i<A.length;i++){
int min=A[i];
for(int j=i;j<A.length;j++){
if(A[j]<=min)
min=A[j];
sum=(sum+min)%(1000000000+7);
}
}
return sum;
}
}
笔者分析3.2
我当然知道不能这么写,一是这思路太普通了,而且肯定超时,但我是真没想出其它方法啊。虽然有个比较好的思路,就是计算每个数再子数组中最小的次数,但没有想出如何实现,不过评论区有人实现了,非常简洁。
class Solution {
public int sumSubarrayMins(int[] A) {
long res = 0;
long mod = 1000000007;
for (int i = 0; i<A.length; i++) {
int l = i-1;
for (; l>=0 && A[i] < A[l]; l--) ;
int r = i+1;
for (; r<A.length && A[i] <= A[r]; r++) ;
res += (i-l)*(r-i)*A[i];
}
return (int)(res % mod);
}
}
题目描述4
笔者解答4.1
class StockSpanner {
Stack<Integer> stack;
Stack<Integer> stack_score;
public StockSpanner() {
stack=new Stack<Integer>();
stack_score=new Stack<Integer>();
}
public int next(int price) {
if(stack.isEmpty()||stack.peek()>price){
stack.push(price);
stack_score.push(1);
return 1;
}else{
int temp_score=1;
while(!stack.isEmpty()&&price>=stack.peek()){
stack.pop();
temp_score+=stack_score.pop();
}
stack.push(price);
stack_score.push(temp_score);
return temp_score;
}
}
}
笔者分析4.2
也算是挽回点颜面,在比较客观地执行用时和内存消耗内完成了这题,主要是这题简单,跟我没啥关系。。。用了两个栈,因为以前的数据要保留,而且要想提高效率,还得用另一个栈保存每个点的得分。主体思想还是单调栈,保证栈内不增即可。有想过用其它数据结构,但发现还是栈比较合适。
题目描述5
笔者解答5.1
class Solution {
public int longestWPI(int[] hours) {
int n = hours.length;
for(int i = 0; i < n; i++){
hours[i] = hours[i] > 8 ? 1 : -1;
}
int res = 0;
for(int i = 0; i < n; i++){
int count = 0;
for(int j = i; j < n; j++){
count += hours[j];
if(count > 0)
res = Math.max(res, j - i + 1);
}
if(n - i <= res)
return res;
}
return res;
}
}
笔者分析5.2
说实话这题我没有想到什么好的办法,和暴力解没什么区别,很意外的是它竟然没有判我超时,也许有可取之处吧。大致思路是,这里的数据值大小反应的是两种状态,数值本身没有意义,所以我们可以统一的用1和-1来表示两种状态,而 依题目意思,就成了在一组0,1序列中,找出最长的一段,该段的和大于0。
题目描述6
笔者解答6.1
class Solution {
public String minRemoveToMakeValid(String s) {
Stack<Integer> stack=new Stack<Integer>();
int i;
for(i=0;i<s.length();i++){
if(stack.isEmpty()){
if(s.charAt(i)=='(')
stack.push(-1-i);
else if(s.charAt(i)==')'){
stack.push(i);
}
}else{
if(stack.peek()<0)
{
if(s.charAt(i)=='(')
stack.push(-1-i);
else if(s.charAt(i)==')')
stack.pop();
}
else{
if(s.charAt(i)=='(')
stack.push(-1-i);
else if(s.charAt(i)==')')
stack.push(i);
}
}
}
if(stack.isEmpty())
return s;
int[] num=new int[s.length()];
int length=0;
while(!stack.isEmpty()){
if(stack.peek()>=0)
num[length]=stack.pop();
else
num[length]=-1-stack.pop();
length++;
}
String str="";
for(i=0;i<s.length();i++){
if(length>0&&i==num[length-1]){
length--;
} else{
str+=s.charAt(i)+"";
}
}
return str;
}
}
笔者分析6.2
虽然是写出来了,但执行时间和内存花销都很大,期间还调整了很多bug,反正就很烂吧。只不过想到了一个方法来在一个栈中既保存左括号和右括号,又保存它们的位置索引,就是用正负来区分是左括号还是右括号,绝对值表示它们的位置,这里特别强调索引为0的位置,如果只用正负的话这个位置是无法确定是左括号还是右括号的。所以在上述代码中,我用了-1代替0,而且它的索引值也不再是绝对值了,需要用-1来减。
总结
栈的中等难度三十题完结了,明天开始困难难度,每日十题打卡第四天,下图为证。