Xử lý tách chữ số từ xâu là một kỹ thuật cần biết trong để xử lý chữ số xuất hiện các xâu, kỹ thuật này cần thiết trong các bài toán xử lý chuỗi, trích xuất thông tin số học, và tiền xử lý dữ liệu. Trong bài viết này, chúng ta sẽ xem xét ba cách phổ biến để xử lý tách chữ số từ một xâu bất kỳ.
1. Kỹ thuật chung
Để xử lý tách chữ số từ một xâu trong C++, chúng ta dựa trên các ý tưởng cơ sở sau:
- Dùng bảng mã ASCII để kiểm tra xem một ký tự có phải là chữ số hay không (
'0' <= c && c <= '9'). - Ghép các chữ số thành một số duy nhất hoặc nhóm các số liên tiếp vào một danh sách.
- Sử dụng các cấu trúc dữ liệu như
stringvàvector<int>để lưu trữ kết quả. - Xử lý theo yêu cầu cụ thể bằng cách thay đổi cách ghép hoặc tách số.
2. Phân tích dựa trên ví dụ
2.1. Ghép tất cả các chữ số từ xâu thành một giá trị duy nhất
Input: Chuỗi S.
Output: Số nguyên là kết quả ghép tất cả các chữ số trong S.
- Khởi tạo biến
result = 0. - Duyệt từng ký tự
ctrongS(theo thứ tự trái → phải):- Nếu
cnằm trong['0'..'9']:- Chuyển
csang giá trị số:digit = c - '0'(từ 0 tới 9). - Cập nhật
result = result * 10 + digitđể “đẩy” các chữ số cũ sang trái một hàng và thêm chữ số mới vào cuối.
- Chuyển
- Nếu
ckhông phải chữ số: bỏ qua.
- Nếu
- Kết thúc duyệt, trả về
result.
Độ phức tạp:
- Thời gian: O(n) với n là độ dài chuỗi (mỗi ký tự xử lý 1 lần).
- Bộ nhớ phụ: O(1).
Minh họa:
#include <iostream>
#include <string>
using namespace std;
int extractNumber(const string& S) {
int result = 0;
for (char c : S) {
if ('0' <= c && c <= '9') {
result = result*10 + (c-'0');
}
}
return result;
}
int main() {
string S = "ab123abc2";
cout << "So ghep duoc: " << extractNumber(S) << endl;
return 0;
}
2.2. Tách các số từ xâu với mỗi số là dãy ký số liên tiếp
Ý tưởng: Duyệt chuỗi trái→phải, tích lũy chữ số liên tiếp vào một biến số nguyên; khi gặp ký tự không phải chữ số thì “chốt” số đang tích lũy và bắt đầu phiên tích lũy mới.
Input: Chuỗi S.
Output: Danh sách (vector) các số nguyên tách được từ chuỗi.
Biến dùng:
vector<int> nums– danh sách kết quả.int num = 0– số đang tích lũy.bool inNumber = false– cờ cho biết có đang ở trong “vùng chữ số” không.
Xử lý:
Duyệt từng ký tự c trong S:
- Nếu
clà chữ số ('0' <= c <= '9'):
→ Cập nhậtnum = num * 10 + (c - '0').
→ ĐặtinNumber = true. - Nếu
ckhông phải chữ số vàinNumber == true:
→ Đưa sốnumvào vectornums.
→ Resetnum = 0,inNumber = false. - Nếu
ckhông phải chữ số vàinNumber == false:
→ Bỏ qua.
Sau vòng lặp, nếu inNumber == true (tức chuỗi kết thúc bằng chữ số):
→ Đưa num vào vector nums.
Trả về vector nums.
Ví dụ:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
vector<int> extractNumbers(const string& S) {
vector<int> nums;
int num = 0; // Biến tích lũy số hiện tại
bool inNumber = false; // Cờ đánh dấu đang ở trong một chuỗi số
for (char c : S) {
if ('0' <= c && c <= '9') {
// Nếu là chữ số, tích lũy vào num
num = num * 10 + (c - '0');
inNumber = true;
} else {
// Nếu gặp ký tự không phải số và đang tích lũy số
if (inNumber) {
nums.push_back(num); // Lưu số vừa hoàn thành
num = 0; // Reset lại num
inNumber = false; // Reset trạng thái
}
}
}
// Sau vòng lặp, nếu kết thúc bằng một số thì phải lưu nốt
if (inNumber) nums.push_back(num);
return nums;
}
int main() {
string S = "ab123abc2";
vector<int> nums = extractNumbers(S);
cout << "Cac so tach duoc: ";
for (int num : nums) {
cout << num << " ";
}
cout << endl;
return 0;
}
2.3. Tách tất cả các số có thể tạo thành từ dãy ký số liên tiếp
Input: Chuỗi sss.
Output: Danh sách (vector) các “số con” duy nhất sinh ra từ mọi đoạn chữ số liên tiếp trong sss, liệt kê theo độ dài tăng dần trong từng đoạn.
Khởi tạo một vector rỗng nums để chứa kết quả.
Khởi tạo một tập seen (hash set) để loại bỏ trùng lặp số con.
Duyệt chỉ số i từ 0 đến s.size()-1:
- Nếu
s[i]là chữ số:
→ Xác định đoạn số liên tiếp bắt đầu tạii: đặtj = ivà tăngjcho đến khij == nhoặcs[j]không phải chữ số. Khi đó, đoạn làs[i..j-1]có độ dàiL = j - i.
→ Sinh tất cả số con của đoạn theo độ dài tăng dần:
Vớilentừ 1 đếnL:
Khởi tạoval = 0.
Với mỗi vị trí bắt đầuptừiđếni + L - len:
Cập nhậtvaltheo chuỗi cons[p..p+len-1]bằng tích lũy: đặtval = 0, rồi vớiqtừpđếnp+len-1, mỗi bướcval = val*10 + (s[q]-'0').
Nếuvalchưa có trongseen, thêmvalvàoseenvà đẩy vàonums.
→ Gáni = jđể tiếp tục duyệt sau đoạn số. - Nếu
s[i]không phải chữ số: tăngilên 1.
Trả về nums.
Minh họa:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_set>
using namespace std;
vector<int> extractUniqueSubNumbers(const string& s) {
vector<int> nums;
unordered_set<int> seen;
int n = s.size();
for (int i = 0; i < n; ) {
if ('0' <= s[i] && s[i] <= '9') {
int j = i;
while (j < n && '0' <= s[j] && s[j] <= '9') j++;
// đoạn số liên tiếp là s[i..j-1]
int L = j - i;
// sinh tất cả số con theo độ dài tăng dần
for (int len = 1; len <= L; ++len) {
for (int p = i; p + len - 1 < j; ++p) {
int val = 0;
for (int q = p; q < p + len; ++q) {
val = val * 10 + (s[q] - '0');
}
if (seen.find(val) == seen.end()) {
seen.insert(val);
nums.push_back(val);
}
}
}
i = j; // nhảy qua đoạn số
} else {
i++;
}
}
return nums;
}
int main() {
string s = "ab123abc2";
vector<int> nums = extractUniqueSubNumbers(s);
cout << "Output: [";
for (int i = 0; i < (int)nums.size(); i++) {
cout << nums[i];
if (i + 1 < (int)nums.size()) cout << ",";
}
cout << "]\n";
return 0;
}
Kết luận
Trong bài viết này, chúng ta đã tìm hiểu ba ví dụ khác nhau để trích xuất số từ xâu :
- Ghép tất cả chữ số thành một số duy nhất
- Tách các dãy chữ số liên tiếp thành một danh sách
- Tạo tất cả các số con có thể từ dãy chữ số liên tiếp
Hy vọng thông qua ba ví dụ này, bạn có thể tự xây dựng cho mình các phương thức khác để giải quyết các yêu cầu trong quá trình giải các bài toán liên quan.
