C18: Xử lý xâu nâng cao
Tác giả: Hà Trí Kiên
Chủ đề: stringstream, getline, chuyển đổi kiểu, pattern thi đấu
1. Tổng quan
Bài này tổng hợp các kỹ thuật xử lý xâu nâng cao trong C++, thường dùng trong thi đấu.
flowchart TD
A["Xử lý xâu nâng cao"] --> B["stringstream"]
A --> C["getline"]
A --> D["Chuyển đổi kiểu"]
A --> E["Xử lý ký tự"]
A --> F["Pattern thi đấu"]
2. stringstream — Phân tích xâu
2.1. Tách từ trong xâu
| #include <bits/stdc++.h>
using namespace std;
int main() {
string s = "Hello World C++ Programming";
stringstream ss(s);
string word;
while (ss >> word) {
cout << word << endl;
}
// Output:
// Hello
// World
// C++
// Programming
return 0;
}
|
2.2. Đọc số từ xâu
| #include <bits/stdc++.h>
using namespace std;
int main() {
string s = "10 20 30 40 50";
stringstream ss(s);
int x;
while (ss >> x) {
cout << x * 2 << " ";
}
cout << endl;
// Output: 20 40 60 80 100
return 0;
}
|
2.3. Tách xâu theo delimiter
| #include <bits/stdc++.h>
using namespace std;
// Tách xâu theo delimiter (không dùng getline)
vector<string> split(const string &s, char delimiter) {
vector<string> tokens;
stringstream ss(s);
string token;
while (getline(ss, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
int main() {
string s = "one,two,three,four";
vector<string> parts = split(s, ',');
for (const string &p : parts) {
cout << "[" << p << "] ";
}
cout << endl;
// Output: [one] [two] [three] [four]
return 0;
}
|
2.4. Định dạng đầu ra với stringstream
| #include <bits/stdc++.h>
using namespace std;
int main() {
stringstream ss;
// Nối xâu
ss << "Hello" << " " << "World" << " " << 42;
cout << ss.str() << endl; // Hello World 42
// Định dạng số
stringstream ss2;
ss2 << fixed << setprecision(2) << 3.14159;
cout << ss2.str() << endl; // 3.14
// Đệm số 0
stringstream ss3;
ss3 << setw(5) << setfill('0') << 42;
cout << ss3.str() << endl; // 00042
return 0;
}
|
2.5. Làm sạch stringstream
| #include <bits/stdc++.h>
using namespace std;
int main() {
stringstream ss;
ss << "Hello";
cout << ss.str() << endl; // Hello
// Làm sạch để dùng lại
ss.str(""); // Xóa nội dung
ss.clear(); // Xóa trạng thái lỗi
ss << "World";
cout << ss.str() << endl; // World
return 0;
}
|
3. getline — Đọc cả dòng
3.1. Đọc dòng cơ bản
| #include <bits/stdc++.h>
using namespace std;
int main() {
string line;
cout << "Nhap dong: ";
getline(cin, line);
cout << "Ban vua nhap: " << line << endl;
return 0;
}
|
3.2. Đọc nhiều dòng
| #include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
cin.ignore(); // Quan trọng: bỏ qua '\n' còn lại sau cin >> n
vector<string> lines(n);
for (int i = 0; i < n; i++) {
getline(cin, lines[i]);
}
for (const string &line : lines) {
cout << "[" << line << "]" << endl;
}
return 0;
}
|
3.3. getline với delimiter
| #include <bits/stdc++.h>
using namespace std;
int main() {
string s = "apple;banana;cherry;date";
stringstream ss(s);
string token;
// Tách theo ';'
while (getline(ss, token, ';')) {
cout << token << endl;
}
// Output:
// apple
// banana
// cherry
// date
return 0;
}
|
Lưu ý khi dùng getline sau cin
| int n;
cin >> n;
cin.ignore(); // BẮT BUỘC: bỏ qua '\n' còn lại
string line;
getline(cin, line);
|
Nếu quên
cin.ignore(),
getline sẽ đọc dòng trống.
4. Chuyển đổi kiểu
4.1. String → Số
| #include <bits/stdc++.h>
using namespace std;
int main() {
// stoi: string → int
int a = stoi("123");
cout << a << endl; // 123
// stoll: string → long long
long long b = stoll("1234567890123");
cout << b << endl;
// stod: string → double
double c = stod("3.14");
cout << c << endl; // 3.14
// stoi với base
int d = stoi("FF", nullptr, 16); // Hex → int
cout << d << endl; // 255
int e = stoi("1010", nullptr, 2); // Binary → int
cout << e << endl; // 10
return 0;
}
|
4.2. Số → String
| #include <bits/stdc++.h>
using namespace std;
int main() {
// to_string
string s1 = to_string(123);
string s2 = to_string(3.14);
string s3 = to_string(true);
cout << s1 << endl; // "123"
cout << s2 << endl; // "3.140000"
cout << s3 << endl; // "1"
// Định dạng tốt hơn với stringstream
stringstream ss;
ss << fixed << setprecision(2) << 3.14159;
string s4 = ss.str(); // "3.14"
return 0;
}
|
4.3. Chuyển đổi ký tự
| #include <bits/stdc++.h>
using namespace std;
int main() {
// char → int
char c = '7';
int digit = c - '0'; // 7
// int → char
int n = 7;
char ch = n + '0'; // '7'
// Chữ thường → chữ hoa
string s = "Hello";
transform(s.begin(), s.end(), s.begin(), ::toupper);
cout << s << endl; // HELLO
// Chữ hoa → chữ thường
transform(s.begin(), s.end(), s.begin(), ::tolower);
cout << s << endl; // hello
// Kiểm tra ký tự
cout << isdigit('7') << endl; // 1
cout << isalpha('A') << endl; // 1
cout << isalnum('A') << endl; // 1
cout << isspace(' ') << endl; // 1
return 0;
}
|
5. Xử lý ký tự
5.1. Các hàm kiểm tra ký tự
| #include <bits/stdc++.h>
using namespace std;
int main() {
string s = "Hello, World! 123";
int letters = 0, digits = 0, spaces = 0, others = 0;
for (char c : s) {
if (isalpha(c)) letters++;
else if (isdigit(c)) digits++;
else if (isspace(c)) spaces++;
else others++;
}
cout << "Letters: " << letters << endl; // 10
cout << "Digits: " << digits << endl; // 3
cout << "Spaces: " << spaces << endl; // 3
cout << "Others: " << others << endl; // 2
return 0;
}
|
5.2. Xóa khoảng trắng
| #include <bits/stdc++.h>
using namespace std;
// Xóa khoảng trắng đầu
string ltrim(const string &s) {
size_t start = s.find_first_not_of(" \t\n\r");
return (start == string::npos) ? "" : s.substr(start);
}
// Xóa khoảng trắng cuối
string rtrim(const string &s) {
size_t end = s.find_last_not_of(" \t\n\r");
return (end == string::npos) ? "" : s.substr(0, end + 1);
}
// Xóa khoảng trắng đầu và cuối
string trim(const string &s) {
return ltrim(rtrim(s));
}
// Xóa tất cả khoảng trắng
string removeSpaces(const string &s) {
string result;
for (char c : s) {
if (!isspace(c)) result += c;
}
return result;
}
|
6. Pattern thi đấu
6.1. Đếm tần suất ký tự
| #include <bits/stdc++.h>
using namespace std;
int main() {
string s = "abracadabra";
// Cách 1: map
map<char, int> freq;
for (char c : s) freq[c]++;
for (auto [ch, cnt] : freq) {
cout << ch << ": " << cnt << endl;
}
// Cách 2: mảng (nhanh hơn cho ASCII)
int cnt[256] = {0};
for (char c : s) cnt[(unsigned char)c]++;
return 0;
}
|
6.2. Kiểm tra đảo chữ (Anagram)
| #include <bits/stdc++.h>
using namespace std;
bool isAnagram(string a, string b) {
if (a.length() != b.length()) return false;
sort(a.begin(), a.end());
sort(b.begin(), b.end());
return a == b;
}
// Cách nhanh hơn: dùng mảng đếm
bool isAnagramFast(const string &a, const string &b) {
if (a.length() != b.length()) return false;
int cnt[26] = {0};
for (char c : a) cnt[c - 'a']++;
for (char c : b) cnt[c - 'a']--;
for (int i = 0; i < 26; i++) {
if (cnt[i] != 0) return false;
}
return true;
}
|
6.3. Tìm xâu con dài nhất không lặp ký tự
| #include <bits/stdc++.h>
using namespace std;
int lengthOfLongestSubstring(const string &s) {
int n = s.length();
int ans = 0;
int last[256];
fill(last, last + 256, -1);
int left = 0;
for (int right = 0; right < n; right++) {
left = max(left, last[(unsigned char)s[right]] + 1);
ans = max(ans, right - left + 1);
last[(unsigned char)s[right]] = right;
}
return ans;
}
int main() {
cout << lengthOfLongestSubstring("abcabcbb") << endl; // 3
cout << lengthOfLongestSubstring("bbbbb") << endl; // 1
return 0;
}
|
6.4. So sánh xâu với wildcard
| #include <bits/stdc++.h>
using namespace std;
// So sánh xâu với '?' ( khớp 1 ký tự bất kỳ)
bool match(const string &s, const string &pattern) {
int n = s.length(), m = pattern.length();
vector<vector<bool>> dp(n + 1, vector<bool>(m + 1, false));
dp[0][0] = true;
for (int j = 1; j <= m; j++) {
if (pattern[j - 1] == '*') dp[0][j] = dp[0][j - 1];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (pattern[j - 1] == '*') {
dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
} else if (pattern[j - 1] == '?' || s[i - 1] == pattern[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
}
}
return dp[n][m];
}
|
7. Bài tập thực hành
Bài 1: Đếm từ
Đọc một dòng văn bản. In ra số từ trong dòng.
Lời giải
| #include <bits/stdc++.h>
using namespace std;
int main() {
string line;
getline(cin, line);
stringstream ss(line);
string word;
int count = 0;
while (ss >> word) count++;
cout << count << endl;
return 0;
}
|
Bài 2: Chuyển đổi số
Đọc một dòng chứa các số nguyên cách nhau bởi dấu phẩy. In ra tổng.
Lời giải
| #include <bits/stdc++.h>
using namespace std;
int main() {
string line;
getline(cin, line);
stringstream ss(line);
string token;
int sum = 0;
while (getline(ss, token, ',')) {
sum += stoi(token);
}
cout << sum << endl;
return 0;
}
|
Bài 3: Kiểm tra palindrome
Đọc chuỗi, bỏ qua khoảng trắng và ký tự đặc biệt. Kiểm tra palindrome.
Lời giải
| #include <bits/stdc++.h>
using namespace std;
int main() {
string line;
getline(cin, line);
string clean;
for (char c : line) {
if (isalnum(c)) clean += tolower(c);
}
string rev = clean;
reverse(rev.begin(), rev.end());
cout << (clean == rev ? "Yes" : "No") << endl;
return 0;
}
|
Bài viết liên quan
Bài tiếp theo: C19: Matrix & Grid Pattern →