Hướng dẫn giải của Sub Bracket
Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Tác giả:
Lời giải của
#include <bits/stdc++.h>
using namespace std;
#define NAME "bracket"
#define show(x) cout << (#x) << " is " << (x) << endl
#define ll long long
#define ms(arr,val) memset(arr,val,sizeof(arr))
#define len length()
#define epb emplace_back
#define fir first
#define sed second
#define sz(ds) (int)ds.size()
const int maxn = 2e5;
int main(){
//ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
//ifstream cin(NAME".inp");
//ofstream cout(NAME".out");
string s;
cin>>s;
vector<int> st;
for(int i = 0 ; i < s.len ; i++){
if( !st.empty() && s[st.back()] == '(' && s[i] == ')' )
st.pop_back();
else
st.push_back(i);
}
if (st.empty()){
cout<<s.len<<" "<<1<<endl;
return 0;
}
vector<int> seg;
if (st[0] != 0) seg.epb(-1);
for(auto x : st) seg.epb(x);
if (st.back() != s.len-1) seg.epb(s.len);
map<int,int> cnt;
for( int i = 1 ; i < sz(seg) ; i++ ){
int l = seg[i] - seg[i-1] - 1;
if ( l >= 1) cnt[ l ]++;
}
if(cnt.size() != 0){
cout<<(--cnt.end())->fir<<" "<<(--cnt.end())->sed<<endl;
}else{
cout<<0<<" "<<1<<endl;
}
}
Lời giải của
- Trước tiên ta sẽ tóm tắt lại đề bài:
- Đề bài yêu cầu là hãy tìm chuỗi ngoặc đúng có độ dài là lớn nhất --> tiếp đó là đếm trong chuỗi ban đầu có bao nhiêu chuỗi ngoặc đúng mà có độ dài bằng với chuỗi ngoặc đúng có độ dài lớn nhất
- Ví dụ: cho chuỗi ngoặc ) ()()() ) (()) ))) () ) (()())
- ta có có thể tách ra thành: # ) # ()()() # ) # (()) # ))) # () # ) # (()()) #
- Ta kí hiệu dấu thăng (#) như một bức tưởng ngăn cách nha.
- Trong ví dụ trên, ta ta nhận thấy có các chuỗi ngoặc đúng lần lượt là: ()()() , (()) , () và (()()) .
Nhưng đề bài chỉ yêu cầu là lấy chuỗi ngoặc đúng dài nhất và đếm nó thôi, nên đáp án của bài này có 2 chuỗi ngoặc đúng dài nhất có độ dài là 6 là: ()()() và (()()). Nên đáp án in ra sẽ là 6 2
Ví dụ khác: ) ) (
- ta có có thể tách ra thành: # ) # ) # ( #
- không có chuỗi ngoặc đúng nào nên đáp án in ra theo đề bài yêu cầu là: độ dài 0 số lượng 1
Giải thích thêm về khái niệm chuỗi ngoặc đúng
- Chuỗi ngoặc đúng hiểu đơn giản là khi có một cái dấu mở ngoặc thì phải có một dấu đóng ngoặc phù hợp với nó.
- (Nếu bị rối với từ đóng ngoặc, mở ngoặc thì có thể thay thế bằng việc vẽ hình bằng biểu đồ cho dễ nhìn - dấu mở ngoặc thì vẽ mũi tên hướng lên 45 độ, nêu tiếp tục là mở ngoặc thì nối tiếp vào vị trí cuối của mũi tên vừa rồi vẽ lên 45 độ nữa. Nếu là đóng ngoặc thì từ vị trí cuối của mũi tên phía trước vẽ xuống dưới 45 độ. Nhìn vào hình vừa vẽ ta vẽ các đường ngang (nếu có điểm một phần đồ thị đi lên đường ngang và đi xuống đường ngang thì nó sẽ tạo thành một chuỗi ngoặc con đúng (đối chiếu với chuỗi ngoặc sẽ thấy))).
Code
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio (0); cin.tie (0);
// Đoạn code dưới đây: Comment giải thích được viết ở bên phải của dòng code
string s; cin >> s; // chuỗi ngoặc cần kiểm tra
vector <int> p; // p dùng để lưu những vị trí dấu ngoặc sai (hoặc chưa đúng) - ta có thể thay thế stack bằng vector (vì vector có thể thêm phần tử vào cuối, và xóa phần tử ở cuối, và tốc độ của stack và vector là như nhau)
p.push_back(-1); // cho vào -1 coi như một điểm chốt, code sẽ dễ hơn trong bài này
int maxLength=0; // độ dài chuỗi con đúng dài nhất
int cntMaxLength=1; // số lượng chuỗi con đúng dài nhất
char open='('; // tạo một tên biến mở ngoặc, đóng ngoặc thì đọc code có thể dễ hiểu hơn
char close=')'; // ví dụ thay vì viết if(s[i]=='(') thì viết s[i]==open (có lẽ dễ đọc hơn và ít sai hơn)
function <void(int)> // đây là một cái hàm đơn giản: truyền vào độ dài đúng chuỗi con đúng hiện tại, nếu độ dài chuỗi con hiện tại bằng độ dài lớn nhất thì đếm+1, ngược lại nếu độ dài hiện tại lớn hơn max thì gán max bằng độ dài hiện tại, và reset đếm=1
updateAns = [&](int len){
if(len==maxLength){
cntMaxLength+=1;
}
else if(len>maxLength){
maxLength=len;
cntMaxLength=1;
}
};
for(int i=0, sz=s.size(); i<sz; ++i){ // duyệt từ đầu tới cuối xâu
if(s[i]==open){ // nếu kí tự là mở ngoặc... ((((( ta nhận thấy là chỉ có dấu mở ngoặc thì chẳng tạo được thành chuỗi xâu ngoặc đúng, nên kí tự mở ngoặc đương nhiên là một vị trí sai, và ta chèn vị trí sai đó vào cuối của p.
p.push_back(i);
}
else if(s[i]==close){ // ngược lại thì mặc nhiên nó sẽ là dấu đóng ngoặc rồi (input trong lập trình sẽ không cho những thứ sai yêu cầu đâu) - nhưng ta viết thêm dòng code này vào cho dễ đọc hơn thôi - và nó không ảnh hướng tới tốc độ chương trình lắm
if(p.back()!=-1 && s[p.back()]==open){ // là đóng ngoặc rồi thì ta kiểm tra xem nếu vị trí sai cuối cùng có phải là dấu mở ngoặc không (nếu là mở ngoặc thì nó sẽ tạo thành một chuỗi ngoặc đúng) ví dụ (((( --> có thêm ) thì nó tạo thành một cái đúng, rồi ta xóa cái đúng đi còn lại những dấu ngoặc sai là (((.
p.pop_back();
updateAns(i-p.back()); //do nó tạo thành dấu ngoặc đúng rồi lên ta kiểm tra độ dài của chuỗi ngoặc đúng bằng công thức: vị trí hiện tại là i, trừ cho vị trí sai cuối cùng là v.back() (có -1 ở đầu nên dễ kiểm tra hơn).
}
else{
p.push_back(i); // ngược lại nếu không có dấu mở ngoặc ngoặc (((, mà chỉ còn vị trí sai là -1, hoặc vị trí sai cuối là dấu dóng ngoặc ))), thì chỉ có dấu đóng ngoặc và không có mở ngoặc thì không thể khớp với cái nào cả --> Nên nó tại thành một vị trí sai --> Và ta chèn vị trí sai đó vào p.
}
}
}
cout <<maxLength <<" " <<cntMaxLength <<endl; //in đáp án ra màn hình
return 0;
}
Bình luận