// 9ms, 14.06MB
class Solution {
public:
int minOperations(vector<string>& logs) {
int depth{};
for(auto& log : logs) {
if(log == "../"s) {
if(depth > 0) --depth;
}
else if(log == "./"s) {
// do nothing.
}
else {
++depth;
}
}
return depth;
}
};#include <vector>
#include <string>
int minOperations(std::vector<std::string>& logs) {
int depth = 0;
for (const std::string& log : logs) {
if (log == "../") {
if (depth > 0) {
--depth;
}
} else if (log != "./") {
++depth;
}
}
return depth;
}gpt - 궁금증 묻기
Then I have a question
I got 3ms in this code.
class Solution {
public:
int minOperations(vector<string>& logs) {
int depth{};
for(auto& log : logs) {
if(log.substr(0, 3) == "../"s) {
if(depth > 0) --depth;
}
else if(log.substr(0, 2) == "./"s) {
// do nothing.
}
else {
++depth;
}
}
return depth;
}
};but i got 9ms in this code
class Solution {
public:
int minOperations(vector<string>& logs) {
int depth{};
for(auto& log : logs) {
if(log.length() == 3 && log == "../"s) {
if(depth > 0) --depth;
}
else if(log.length() == 2 && log == "./"s) {
// do nothing.
}
else {
++depth;
}
}
return depth;
}
};I think substr is slow and just check the length makes similar effect, but I don't know why second code takes more time than first code. please explain this?
The difference in execution time between your two codes can be attributed to how C++ handles string comparisons and operations internally.
-
String Comparison (
==) vs. Substring (substr):- In the first code,
log.substr(0, 3) == "../"andlog.substr(0, 2) == "./"create temporary substrings and then compare them. - In the second code,
log.length() == 3 && log == "../"andlog.length() == 2 && log == "./"do direct comparisons.
- In the first code,
-
Short-Circuit Evaluation:
- In the second code, the
log.length()checks are intended to reduce unnecessary comparisons. However, this introduces extra conditional checks (log.length() == 3andlog.length() == 2) before the string comparisons.
- In the second code, the
-
Implementation Details:
- The first code relies on
substrto extract the prefix before comparing, which involves creating a new string object but this can be very efficient due to short-circuit optimizations in modern C++ libraries. - The second code avoids
substrbut introduces additional length checks and direct string comparisons, which could be less optimized than expected.
- The first code relies on
- Substr Optimizations: Modern C++ libraries might optimize
substroperations for short strings, making them surprisingly fast. - Branch Prediction: Additional conditional checks in the second code might affect CPU branch prediction negatively, causing slower performance.
- Inlining and Micro-optimizations: The first code might benefit from better inlining and micro-optimizations by the compiler.
Combining the benefits of both approaches:
class Solution {
public:
int minOperations(vector<string>& logs) {
int depth{};
for(const auto& log : logs) {
if (log == "../") {
if(depth > 0) --depth;
} else if (log != "./") {
++depth;
}
}
return depth;
}
};This approach avoids substr and unnecessary length checks while ensuring concise string comparisons.
Understanding performance implications in C++ often requires a deep dive into compiler optimizations and hardware behaviors. For practical purposes, testing different approaches and profiling your code can provide the most reliable insights.
// 0ms, 9.64MB
class Solution {
public:
int i{};
string reverseParentheses(string s) {
string answer;
for(int e = static_cast<int>(s.length());i<e;++i) {
if(s[i] == '(') {
++i;
answer += reverseParentheses(s);
}
else if(s[i] == ')') {
break;
}
else answer.push_back(s[i]);
}
if(i != static_cast<int>(s.length()))
reverse(begin(answer), end(answer));
return answer;
}
};Got it! Let's dive into how we can solve this problem. We'll use a stack to handle the parentheses and reverse the substrings.
Here's a step-by-step breakdown of the solution:
- Use a stack to help us reverse substrings within parentheses.
- Iterate through each character in the string:
- If the character is '(', push the current string onto the stack and start a new substring.
- If the character is ')', reverse the current substring and append it to the string popped from the stack.
- Otherwise, append the character to the current substring.
- At the end, the current substring will contain the final result.
Here's a sample implementation in C++:
class Solution {
public:
int i{};
string reverseParentheses(string s) {
std::stack<std::string> st;
std::string current;
for (char& ch : s) {
if (ch == '(') {
st.push(current); // 괄호 이전에 쌓은 애들 stack에 누적
current.clear();
} else if (ch == ')') {
std::reverse(current.begin(), current.end()); // 쌓은애들 거꾸로 뒤집기
current = st.top() + current;
st.pop();
} else {
current += ch;
}
}
return current;
}
};비재귀로도 가능~ 훨씬 더 좋은 듯!
- Stack usage: The stack
stkeeps track of the string parts before encountering a '('. - Current substring:
currentholds the ongoing substring that we are processing. - Character check:
- On '(': Push the current substring onto the stack and clear it.
- On ')': Reverse the current substring, pop from the stack, and append the reversed substring to the popped string.
- Otherwise, just append the character to the current substring.
- Final result: After the loop,
currentholds the final result without any parentheses.
This solution efficiently handles the problem constraints and provides the correct output. If you have any questions or need further explanations, feel free to ask!
class Solution {
public:
int maximumGain(string s, int x, int y) {
int answer{};
// x가 더 크면 ab먼저, y가 더 크면 ba 먼저? (같을 때는 상관 없음)
string temp, temp2;
if(x >= y) {
// ab
for(char c : s) {
if(!temp.empty() && temp.back() == 'a' && c == 'b') {
temp.pop_back();
answer += x;
}
else temp.push_back(c);
}
// ba
for(char c : temp) {
if(!temp2.empty() && temp2.back() == 'b' && c == 'a') {
temp2.pop_back();
answer += y;
}
else temp2.push_back(c);
}
}
else {
// ba
for(char c : s) {
if(!temp.empty() && temp.back() == 'b' && c == 'a') {
temp.pop_back();
answer += y;
}
else temp.push_back(c);
}
// ab
for(char c : temp) {
if(!temp2.empty() && temp2.back() == 'a' && c == 'b') {
temp2.pop_back();
answer += x;
}
else temp2.push_back(c);
}
}
return answer;
}
};To solve LeetCode problem 1717, "Maximum Score From Removing Substrings," we need to remove specified substrings from a given string in such a way that the score is maximized. Here’s a step-by-step approach to solve this problem:
You are given a string s and two integers x and y. You need to maximize the score by performing the following operations any number of times:
- Remove the substring
"ab"and gainxpoints. - Remove the substring
"ba"and gainypoints.
-
Determine the Order of Operations:
- If
x > y, prioritize removing"ab"first, because it gives more points. - If
y > x, prioritize removing"ba"first. - If
x == y, it doesn't matter which substring is removed first as the points are the same.
- If
-
Stack-based Solution:
- Use a stack to help efficiently remove the substrings in the desired order.
- Traverse through the string and push characters to the stack while checking for the target substrings.
- Stack Simulation:
- Create a stack to process the string.
- Traverse the string, pushing characters onto the stack.
- Whenever the target substring is formed (either
"ab"or"ba"depending on priority), pop characters from the stack and add the corresponding points to the score.
Here's a complete implementation in C++:
#include <iostream>
#include <string>
#include <stack>
using namespace std;
int maximumGain(string s, int x, int y) {
int score = 0;
stack<char> st;
auto removeSubstring = [&](char a, char b, int points) {
stack<char> tempStack;
for (char c : s) {
if (!st.empty() && st.top() == a && c == b) {
st.pop();
score += points;
} else {
st.push(c);
}
}
// Store the intermediate result back to `s` for the next pass
s = "";
while (!st.empty()) {
tempStack.push(st.top());
st.pop();
}
while (!tempStack.empty()) {
s += tempStack.top();
tempStack.pop();
}
};
// Determine the order based on the given points
if (x > y) {
removeSubstring('a', 'b', x);
removeSubstring('b', 'a', y);
} else {
removeSubstring('b', 'a', y);
removeSubstring('a', 'b', x);
}
return score;
}
int main() {
string s = "cdbcbbaaabab";
int x = 4;
int y = 5;
cout << "Maximum score: " << maximumGain(s, x, y) << endl;
return 0;
}- removeSubstring function: It removes a specific substring (
afollowed byb) from the string using a stack and updates the score. - Order of Removal: The substrings are removed in the order which gives the highest points first.
- Intermediate Storage: The intermediate state of the string after each pass is stored back in
sfor the next removal operation.
This approach ensures that the operations are performed efficiently and the score is maximized based on the given points for each type of substring removal.
[!TIP] Question I have a question in your approach. Please make a proof for Why this is correct.
- If
x > y, prioritize removing"ab"first, because it gives more points.- If
y > x, prioritize removing"ba"first.- If
x == y, it doesn't matter which substring is removed first as the points are the same.
To understand why prioritizing the removal of substrings "ab" or "ba" based on their respective scores (x and y) is the correct approach, let's go through the reasoning step by step.
Given a string s and two scores x and y:
- Removing the substring
"ab"givesxpoints. - Removing the substring
"ba"givesypoints.
The goal is to maximize the total score by removing these substrings in the optimal order.
To maximize the score, we should prioritize removing the substrings that provide the highest points first. The reason for this is based on the greedy algorithm principle: by making the locally optimal choice at each step, we aim to achieve the globally optimal solution.
- When
x > y, removing"ab"first is more beneficial because it provides more points. After removing as many"ab"as possible, we then remove"ba"substrings. - By prioritizing
"ab", we ensure that the high-scoring substrings are removed first, which maximizes the score. Any remaining characters after removing all possible"ab"substrings can then form"ba"substrings, which are removed next.
- When
y > x, the removal of"ba"gives more points. Hence, we should remove all possible"ba"substrings first. After that, we proceed to remove"ab"substrings. - This way, we capitalize on the higher score of
"ba"removals, ensuring that we don't miss out on the higher points before addressing the lower-scoring"ab"removals.
- When
x == y, the order of removal doesn't matter since both operations give the same points. Removing any of the substrings first will lead to the same total score.
Suppose s = "ababbab", x = 5, and y = 3.
-
First Pass: Remove
"ab"substrings:- String:
"ababbab" - After removing
"ab":s = "abbbab" - Score:
+5(1 removal of"ab")
- String:
-
Second Pass: Remove
"ab"again:- String:
"abbbab" - After removing
"ab":s = "bbab" - Score:
+5(1 removal of"ab")
- String:
-
Third Pass: Remove
"ab"again:- String:
"bbab" - After removing
"ab":s = "bb" - Score:
+5(1 removal of"ab")
- String:
-
Final Pass: Remove
"ba"substrings:- String:
"bb" - No more
"ba"substrings can be removed.
- String:
Total Score: 15 (from 3 removals of "ab")
Suppose s = "babaab", x = 2, and y = 4.
-
First Pass: Remove
"ba"substrings:- String:
"babaab" - After removing
"ba":s = "baab" - Score:
+4(1 removal of"ba")
- String:
-
Second Pass: Remove
"ba"again:- String:
"baab" - After removing
"ba":s = "ab" - Score:
+4(1 removal of"ba")
- String:
-
Final Pass: Remove
"ab"substrings:- String:
"ab" - After removing
"ab":s = "" - Score:
+2(1 removal of"ab")
- String:
Total Score: 10 (from 2 removals of "ba" and 1 removal of "ab")