So sánh 2 kí tự trong xâu năm 2024

Trong bảng mã ASCII có những kí tự in được và không in được. Trong chương này, chúng ta sẽ làm việc với các kí tự in được, nhiều nhất là các kí tự chữ cái và chữ số. Nếu muốn tìm hiểu kĩ hơn về bảng mã ASCII đầy đủ, bạn đọc có thể truy cập vào đường link sau: https://vi.wikipedia.org/wiki/ASCII

2. Kí tự và xâu kí tự

Trong máy tính, thông tin được biểu diễn ở dạng số và dạng phi số. Chúng ta đã quá quen thuộc với thông tin dạng số, vậy còn dạng phi số? Đó là văn bản, hình ảnh, âm thanh,...Đối với lập trình thi đấu, thông tin dạng văn bản cũng xuất hiện thường xuyên không kém dạng số, và các ngôn ngữ lập trình đều cung cấp những kiểu dữ liệu để lưu trữ thông tin dạng văn bản. Có hai loại dữ liệu dạng văn bản thường gặp nhất là kiểu kí tự và kiểu xâu kí tự (nhiều kí tự ghép lại với nhau).

Đối với kiểu kí tự, chúng ta có kiểu dữ liệu

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

3 để biểu diễn, còn xâu kí tự thì có hai cách khác nhau:

  • Sử dụng một mảng gồm nhiều phần tử kiểu

    cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

    3.
  • Sử dụng thư viện con

    cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

    5 đã được xây dựng sẵn trong C++. Cách này được ưa chuộng hơn vì thao tác dữ liệu tốt hơn.

II. Thư viện

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5 trong C++

Thư viện chuẩn của C++ cung cấp cho chúng ta một thư viện con

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5 hỗ trợ việc lưu trữ các xâu kí tự (xâu kí tự) và rất nhiều các phương thức xử lý đi kèm.

1. Khai báo và truy cập các phần tử xâu

Để khai báo một xâu sử dụng thư viện

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5, đầu tiên ta cần khai báo thư viện và không gian tên chứa nó bằng cú pháp:


# include 
using namespace std;

Sau đó, khai báo một xâu bằng cú pháp:

string {Tên_xâu};

Vẫn như thường lệ, {Tên_xâu} là một định danh do người dùng đặt ra, miễn là không trùng với từ khóa của hệ thống. Ta không cần khai báo độ dài của xâu, mà mỗi khi thêm một kí tự vào thì

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

9 sẽ tự động điều chỉnh độ dài của xâu cho vừa khớp với số lượng kí tự. Khi khai báo xâu, mặc định xâu đó sẽ là xâu rỗng (không có kí tự nào).

Các kí tự trong xâu sẽ được đánh số từ 00. Để truy cập một vị trí trong xâu (với điều kiện vị trí đó hiện đang có kí tự hoặc đã được khởi tạo), ta dùng cú pháp:

{Tên_xâu}[{Vị_trí}]

Khá giống với mảng đúng không nào! Sau khi truy cập, mỗi vị trí trong xâu có thể được thao tác giống như một kí tự và kết hợp với các câu lệnh cũng như toán tử. Ví dụ, gán một biến cc bằng kí tự ở vị trí số 22 của xâu ss bằng cú pháp:

char c = s[2];

2. Cách nhập xuất một xâu

2.1. Nhập xuất các biến kiểu

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5

Khi nhập xuất một biến xâu kí tự, ta có thể coi xâu đó như một biến đơn và sử dụng hai câu lệnh


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

1 và


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

2 để nhập xuất. Cú pháp như sau:

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

Tuy nhiên, có một lưu ý khi sử dụng lệnh


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

1 đối với

cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu.
cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

9, đó là nếu như trong quá trình nhập liệu gặp phải dấu cách, thì việc nhập sẽ bị ngắt tại đó, cho dù người dùng có nhập thêm bao nhiêu kí tự đi chăng nữa thì xâu cũng sẽ chỉ lưu trữ phần ở phía trước dấu cách mà thôi. Cùng xem ví dụ dưới đây:


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

Nếu người dùng nhập vào một tên là


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

5, thì khi chạy chương trình ta sẽ thu được kết quả này:

Tên vừa nhập là: Vũ

Do đó, trong trường hợp cần đọc vào một xâu có cả dấu cách, ta sẽ sử dụng kết họp hai cú pháp:

getline(cin, {Tên_xâu});

Lệnh


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

6 sẽ thu nhận cả dòng dữ liệu nhập vào, bao gồm cả những dấu cách. Nó sẽ dừng việc đọc lại khi gặp kí tự


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

7 - tức là kí tự xuống dòng:


# include 

# include 
using namespace std;
int main()
{
    string name;
    getline(cin, name);
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

Lúc này, với tên nhập vào là


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

5, chạy chương trình sẽ thu được kết quả chính xác:

Tên vừa nhập là: Vũ Quế Lâm

2.2. Nhập nhiều xâu kí tự hoặc chuyển đổi từ nhập số sang nhập xâu bằng


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

6

Nếu như chúng ta chỉ nhập một xâu kí tự duy nhất, thì sẽ không có điều gì đáng lưu tâm cả, bạn chỉ cần lựa chọn giữa


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

1 và

Tên vừa nhập là: Vũ

1 tùy vào việc xâu bạn nhập vào có dấu cách hay không. Tuy nhiên, khi dữ liệu đầu vào có nhiều xâu kí tự khác nhau, hoặc khi dữ liệu đầu vào bao gồm cả số và xâu, thì câu chuyện sẽ khác đi. Về bản chất, khi các bạn nhập bất cứ thứ gì vào từ bàn phím, chúng sẽ được đẩy vào bộ nhớ đệm, rồi hàm


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

1 sẽ "đọc" dữ liệu ra từ bộ nhớ đệm rồi nạp vào biến.

Chẳng hạn, nếu như các bạn nhập vào một số là 123,123, rồi ấn phím

Tên vừa nhập là: Vũ

3 (chính là kí tự xuống dòng), thì 123123 và kí tự


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

7 sẽ được đẩy vào bộ nhớ đệm trước, sau đó hàm


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

1 mới "quét qua" dữ liệu trong bộ nhớ đệm và đưa nó vào biến phía sau. Trong trường hợp biến phía sau là một biến kiểu số, thì chỉ có các chữ số mới được ghi nhận vào biến, còn những kí tự như

Tên vừa nhập là: Vũ

6 và


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

7 sẽ bị bỏ qua, nhờ đó nên trong trường hợp đọc nhiều số khác nhau, chương trình vẫn sẽ phân tách đúng các số, dù các bạn dùng dấu cách hay dấu xuống dòng. Tuy nhiên, nếu như theo sau số nhập vào là một kí tự, hoặc một xâu kí tự, thì nó sẽ đọc được cả kí tự


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

7 (vì kí tự


# include 

# include 
using namespace std;
int main()
{
    string name;
    cin >> name;
    cout << "Tên vừa nhập là: " << name;
    return 0;
}

7 chỉ bị bỏ qua chứ nó vẫn còn tồn tại trong bộ nhớ đệm), dẫn đến xâu kí tự sẽ bị đọc sai. Cùng xem một ví dụ dưới đây:

string {Tên_xâu};

0

Nếu như nhập vào dữ liệu là id=1id = 1 và name=name=

getline(cin, {Tên_xâu});

0, thì khi chạy chương trình các bạn sẽ thấy kết quả in ra chỉ có như sau:

string {Tên_xâu};

1

Nguyên do là vì, khi nhập xong số 1,1, chúng ta sẽ nhấn

Tên vừa nhập là: Vũ

3 hoặc dấu cách. Biến idid chỉ có thể đọc được số 1,1, còn kí tự dấu cách hoặc xuống dòng vẫn nằm lại trong bộ nhớ đệm. Hàm

getline(cin, {Tên_xâu});

2 tiếp theo sẽ đọc luôn cả những kí tự đó, dẫn đến hai trường hợp sau:

  • Nếu kí tự còn lại là dấu cách, thì kí tự đó sẽ bị thêm vào đầu của xâu kí tự namename.
  • Nếu kí tự còn lại là dấu xuống dòng, thì hàm `

include

include

using namespace std; int main() {

string name;  
cin >> name;  
cout << "Tên vừa nhập là: " << name;  
return 0;  
}
6 sẽ dừng lại luôn và xâu kí tự namename sẽ bị mất giá trị.

Tựu chung lại, dữ liệu của chúng ta sẽ bị sai! Vậy giải pháp là gì? Chúng ta cần xóa bộ nhớ đệm trước khi nhập một xâu (nên làm như vậy dù nhập một xâu hay nhiều xâu). Hàm 

include

include

using namespace std; int main() {

string name;
cin >> name;
cout << "Tên vừa nhập là: " << name;
return 0;
}

1 trong C++ cung cấp phương thức 
getline(cin, {Tên_xâu});

5 để làm điều đó. Cú pháp như sau:
string {Tên_xâu};

2

Phương thức 
getline(cin, {Tên_xâu});

5 sẽ xóa đi nn kí tự trong bộ nhớ đệm cho tới khi gặp kí tự cc thì dừng lại, và luồng nhập dữ liệu sẽ bắt đầu từ kí tự phía sau kí tự cc. Nếu như ta để trống tham số thì chương trính sẽ tự động hiểu là chỉ xóa 11 kí tự trong bộ nhớ đệm. Điều này khá hữu ích khi nhập xâu ngay sau một số, vì chúng ta sẽ có thói quen dùng một dấu cách hoặc một dấu xuống dòng sau khi nhập số. Như vậy, đoạn code phía trên có thể sửa lại như sau:
string {Tên_xâu};

3

Lúc này, kết quả sẽ trở nên chính xác với bộ dữ liệu nhập vào:
string {Tên_xâu};

4

### 2.3\. Xuất ra các hằng xâu hoặc hằng kí tự

Trong trường hợp cần viết các hằng xâu hoặc hằng kí tự trong C++, ta có quy tắc như sau:

  • Nếu viết ra một kí tự, thì đặt kí tự đó trong cặp ngoặc getline(cin, {Tên_xâu}); 7 hoặc getline(cin, {Tên_xâu}); 8\. Ví dụ, viết ra kí tự getline(cin, {Tên_xâu}); 9 thì có thể viết `
# include # include using namespace std; int main() { string name; getline(cin, name); cout << "Tên vừa nhập là: " << name; return 0; }
0 hoặc

# include   

# include   
using namespace std;  
int main()  
{  
    string name;  
    getline(cin, name);  
    cout << "Tên vừa nhập là: " << name;  
    return 0;  
}  
1 đều được.

  • Nếu viết ra một chỗi có nhiều hơn 11 kí tự, thì cần đặt xâu trong cặp dấu

    getline(cin, {Tên_xâu});

    8. Ví dụ, khi muốn viết ra thông báo

    `

include

include

using namespace std; int main() {

string name;  
getline(cin, name);  
cout << "Tên vừa nhập là: " << name;  
return 0;  
}
3 thì phải viết là  

include

include

using namespace std; int main() {

string name;  
getline(cin, name);  
cout << "Tên vừa nhập là: " << name;  
return 0;  
}
4.

## III. Một số thao tác cơ bản trên xâu kí tự

## 1\. Duyệt xâu

Để duyệt qua các phần tử trên xâu, ta sử dụng một vòng lặp từ vị trí đầu tiên tới vị trí cuối cùng của xâu. Thư viện 
cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5 cung cấp cú pháp 

include

include

using namespace std; int main() {

string name;
getline(cin, name);
cout << "Tên vừa nhập là: " << name;
return 0;
}

6 để lấy độ dài của xâu, mà ta biết rằng các phần tử trong xâu được đánh số từ vị trí 0,0, nên cú pháp duyệt xâu như sau:
string {Tên_xâu};

5

Chẳng hạn, để duyệt các phần tử của một xâu ss từ đầu tới cuối xâu, ta viết như sau:
string {Tên_xâu};

6

Nếu muốn duyệt một đoạn nhỏ trên xâu, hoặc duyệt ngược từ cuối về đầu xâu chẳng hạn, chỉ cần biến đổi vòng lặp một chút xíu. Bạn đọc hãy thử tự suy nghĩ về vấn đề này nhé.

Ngoài ra, chúng ta còn có thể duyệt qua tất cả các phần tử trong xâu theo cú pháp duyệt trực tiếp phần tử như sau:
string {Tên_xâu};

7

Ví dụ, muốn duyệt qua mọi phần tử của xâu ss bất kỳ theo cách này, ta viết:
string {Tên_xâu};

8

Tuy nhiên, cách duyệt này chỉ có thể duyệt được mọi phần tử của xâu theo thứ tự từ trái qua phải, và buộc phải duyệt qua tất cả xâu. Vì thế nó không được ưu tiên như cách thứ nhất.

## 2\. Tìm kiếm tuần tự trên xâu

Bài toán đặt ra rất đơn giản: Cho xâu kí tự ss chỉ gồm toàn chữ cái latin in thường và một kí tự chữ cái cc bất kỳ, hãy đếm số lượng kí tự cc trong xâu s?s?

Bằng cách duyệt qua các phần tử trên xâu và áp dụng các toán tử, ta có thể giải quyết bài toán này như sau:
string {Tên_xâu};

9

Bài toán tìm kiếm trên xâu có thể biến đổi linh hoạt theo nhiều cách khác nhau, chỉ cần các bạn hiểu rõ về cách đánh số thứ tự của kí tự và cách duyệt qua các kí tự trong xâu là sẽ làm được. Hãy sang phần bài tập áp dụng để vận dụng bài tập thêm nhé!

## IV. Các thao tác xử lý xâu kí tự

## 1\. Phép so sánh

Như đã đề cập ở phần I, máy tính sử dụng một bảng chữ cái gồm 256256 kí tự được đánh số từ 00 tới 255,255, mỗi kí tự đều được mã hóa bằng những bit nhị phân, gọi là bảng mã ASCII. Hai xâu kí tự được so sánh với nhau dựa trên bảng mã này. Quy trình so sánh hai xâu kí tự XX và YY trong C++ diễn ra như sau:

  • Các kí tự được đánh số từ 00 ở mỗi xâu, sau đó tìm vị trí ii đầu tiên sao cho Xi≠YiX\_i \\ne Y\_i. Khi đó, nếu XiX\_i nằm sau YiY\_i trong bảng mã ASCII thì xâu XX sẽ lớn hơn xâu Y,Y, ngược lại xâu YY lớn hơn xâu XX.
  • Trong trường hợp không tìm được vị trí ii thỏa mãn thì xâu nào dài hơn sẽ là xâu lớn hơn.
  • Nếu cả hai trường hợp trên không xảy ra thì kết luận xâu XX bằng xâu YY.
Các toán tử

include

include

using namespace std; int main() {

string name;
getline(cin, name);
cout << "Tên vừa nhập là: " << name;
return 0;
}

7 có thể được sử dụng trực tiếp để so sánh hai kí tự hoặc hai xâu trong C++, tất nhiên là theo quy tắc nêu trên vì hệ thống đã có sẵn các toán tử so sánh nạp chồng cho kiểu xâu.

Ví dụ 1: Chương trình dưới đây sẽ so sánh hai xâu kí tự và đưa ra xâu lớn hơn
{Tên_xâu}[{Vị_trí}]

0

Kết quả chạy chương trình sẽ là:
{Tên_xâu}[{Vị_trí}]

1

Chuỗi 

include

include

using namespace std; int main() {

string name;
getline(cin, name);
cout << "Tên vừa nhập là: " << name;
return 0;
}

8 nhỏ hơn xâu 

include

include

using namespace std; int main() {

string name;
getline(cin, name);
cout << "Tên vừa nhập là: " << name;
return 0;
}

9 vì kí tự 
Tên vừa nhập là: Vũ Quế Lâm

0 lớn hơn kí tự 
Tên vừa nhập là: Vũ Quế Lâm

1 trong bảng mã ASCII. Một điều rất thú vị trong so sánh xâu, đó là mặc dù số 100100 lớn hơn số 90,90, nhưng xâu 
Tên vừa nhập là: Vũ Quế Lâm

2 sẽ nhỏ hơn xâu 
Tên vừa nhập là: Vũ Quế Lâm

3, vì kí tự 
Tên vừa nhập là: Vũ Quế Lâm

4 đứng trước kí tự 
Tên vừa nhập là: Vũ Quế Lâm

5 trong bảng mã ASCII. Do đó, khi so sánh các số ở dạng xâu cần hết sức chú ý (vấn đề này sẽ được nhắc tới trong chủ đề Xử lý số nguyên lớn ở phần lập trình thi đấu).

Ví dụ 2: In ra các kí tự chữ cái latin in thường trên một dòng (không có dấu cách):
{Tên_xâu}[{Vị_trí}]

2

Kết quả chạy chương trình:
{Tên_xâu}[{Vị_trí}]

3

## 2\. Phép nối xâu

Khác với phép cộng ở kiểu số, toán tử 
Tên vừa nhập là: Vũ Quế Lâm

6 khi được kết hợp với hai xâu có ý nghĩa là nối hai xâu đó với nhau. Ví dụ dưới đây có thể làm rõ:
{Tên_xâu}[{Vị_trí}]

4

Kết quả chạy chương trình sẽ là:
{Tên_xâu}[{Vị_trí}]

5

Lưu ý:

  • Đặc điểm của phép cộng xâu là xâu đứng sau toán tử Tên vừa nhập là: Vũ Quế Lâm 6 sẽ được nối vào phía sau của xâu đứng trước toán tử Tên vừa nhập là: Vũ Quế Lâm 6\. Ví dụ, nếu như ta viết Tên vừa nhập là: Vũ Quế Lâm 9 ở chương trình trên, thì kết quả sẽ trả ra là string {Tên_xâu}; 00 thay vì string {Tên_xâu}; 01
  • Phép nối xâu bản chất là tạo ra một bản sao của xâu ban đầu, nối bản sao đó với xâu cần nối rồi gán ngược trở lại xâu ban đầu. Vì vậy, phép nối bằng toán tử Tên vừa nhập là: Vũ Quế Lâm 6 sẽ có thời gian chạy khá lâu trong các trường hợp xâu dài, cần hết sức lưu ý khi sử dụng.
## 3\. Lấy số hiệu trong bảng mã ASCII của một kí tự Bằng kĩ thuật ép kiểu, ta có thể xác định được số thứ tự trong bảng mã ASCII của một kí tự cc bất kỳ, với cc là một biến kí tự hoặc hằng kí tự. Cú pháp như sau:
{Tên_xâu}[{Vị_trí}]

6

Nếu cc là một hằng kí tự thì ta cần đặt nó trong cặp dấu 
getline(cin, {Tên_xâu});

7, còn nếu là biến kí tự thì không cần. Ví dụ, muốn biết số thứ tự của kí tự 
getline(cin, {Tên_xâu});

9, ta có câu lệnh
string {Tên_xâu};

05 sẽ trả ra kết quả 97,97, còn nếu muốn biết số thứ tự của một biến kí tự c,c, thì chỉ cần viết 
string {Tên_xâu};

06 thôi. Số hiệu của kí tự phải được sử dụng trong các câu lệnh chứ không được đặt nó đứng đơn lẻ.

Hoàn toàn tương tự, ta có thể xác định được kí tự ứng với số hiệu xx trong bảng mã ASCII bằng cú pháp ép kiểu 
cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

3 ngược lại:
{Tên_xâu}[{Vị_trí}]

7

Chẳng hạn, câu lệnh 
string {Tên_xâu};

08 sẽ trả ra kết quả là kí tự chữ số 
string {Tên_xâu};

09.

Có rất nhiều bài tập ứng dụng phần lấy số hiệu kí tự này, chẳng hạn như đổi từ kí tự số sang số đếm được, hay đổi chữ in hoa thành in thường và ngược lại,...thông qua việc đánh giá chênh lệch giữa số hiệu ASCII của các kí tự.

## 4\. Các hàm xử lý xâu có sẵn trong thư viện của C++

Giả sử ta khai báo một xâu kí tự ss bằng cú pháp: 
string {Tên_xâu};

10\. Bảng dưới đây liệt kê những phương thức xử lý dữ liệu thường dùng nhất, được hỗ trợ sẵn trong thư viện con 
cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5 dành cho xâu ss:

![](https://https://i0.wp.com/i.imgur.com/GF64IHR.png)

Thư viện 
cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5 cũng cung cấp các hàm liên quan tới chuyển đổi giữa xâu - số ở bảng dưới đây:

![](https://https://i0.wp.com/i.imgur.com/TGQpBhm.png)

Ngoài ra còn rất nhiều phương thức khác được xây dựng sẵn để hỗ trợ người dùng, bạn đọc có thể tra cứu ở địa chỉ: Lớp string trong C++.

## 5\. Xóa các kí tự trong xâu

Như bạn đọc đã thấy ở mục 5,5, khi cần xóa một kí tự hoặc một xâu con trong xâu ban đầu, ta có thể sử dụng hàm 
string {Tên_xâu};

13 của thư viện 
cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5\. Tuy nhiên, khi xóa các kí tự trong xâu, thì sẽ xảy ra tình huống là các kí tự phía sau đoạn được xóa sẽ đẩy lên phía trên và nối liền với đoạn phía trước, dẫn đến số thứ tự của các kí tự trong xâu được đánh số lại. Nếu không cẩn thận khi xử lý sẽ rất dễ đưa ra kết quả sai. Cùng xem một ví dụ dưới đây, ta sẽ xóa các dấu cách trong một xâu và đưa ra xâu đó sau khi xóa:
{Tên_xâu}[{Vị_trí}]

8

Nếu chạy đoạn chương trình trên với ss bằng 
string {Tên_xâu};

15, kết quả trả về sẽ như sau:
{Tên_xâu}[{Vị_trí}]

9

Ta thấy kết quả hoàn toàn sai, điều này là do các kí tự bị đánh số lại, chiều dài xâu cũng thay đổi mỗi khi xóa dẫn đến vị trí của các dấu cách cũng thay đổi theo. Để khắc phục điểm này, khi xóa các kí tự hoặc xâu con trong một xâu, hãy xóa từ phải qua trái, và luôn đảm bảo rằng phần xâu phía sau đoạn bị xóa đi ở mỗi lần xóa sẽ không còn cần sử dụng đến nữa!
char c = s[2];

0

Với đoạn code mới này, kết quả sẽ trả ra hoàn toàn chính xác:
char c = s[2];

1

## V. Chuỗi kí tự theo phong cách ngôn ngữ C (đọc thêm)

Vì C++ có nền tảng là ngôn ngữ C, nên cũng hỗ trợ xử lý xâu kí tự theo phong cách ngôn ngữ C. Trong C, xâu kí tự được biểu diễn dưới dạng một mảng chứa các kí tự. Cú pháp để khai báo xâu phong cách C là:
char c = s[2];

2

Các kí tự trong xâu kiểu C vẫn được đánh số từ 00. Vì nó là mảng nên cách sử dụng cũng giống như mảng thông thường. Ví dụ khai báo một xâu 
string {Tên_xâu};

16 theo phong cách C, ta có thể viết theo quy tắc khởi tạo mảng như sau:
char c = s[2];

3

hoặc viết theo quy tắc khởi tạo xâu, thì kích thước xâu sẽ tự động điều chỉnh cho khớp với số lượng kí tự:
char c = s[2];

4

Các hàm xử lý với xâu theo phong cách C được hỗ trợ không nhiều, được liệt kê ở bảng dưới đây. Nói chung ta nên ưu tiên sử dụng thư viện 
cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu.

5 vì nó hỗ trợ xử lý xâu cực kỳ tốt, đặc biệt trong các bài toán lập trình thi đấu cần yêu cầu tốc độ lập trình nhanh.

![](https://https://i0.wp.com/i.imgur.com/rATp2Hb.png)

## VI. Một số bài toán đơn giản về xâu kí tự

## 1\. Xâu đối xứng

### Đề bài

Một xâu kí tự được gọi là đối xứng nếu như khi viết ngược nó lại, ta vẫn thu được một xâu mới giống xâu ban đầu. Chẳng hạn, 
string {Tên_xâu};

18, 
string {Tên_xâu};

19,...là các xâu đối xứng, còn các xâu 
getline(cin, {Tên_xâu});

9, 
string {Tên_xâu};

21, 
string {Tên_xâu};

22,...thì không phải.

Yêu cầu: Cho trước một xâu kí tự s,s, hãy xác định xâu đó có phải đối xứng hay không?

Input:

  • Một dòng duy nhất chứa xâu kí tự ss chỉ bao gồm các chữ cái latin in thường.
Ràng buộc:
  • Độ dài xâu ss không vượt quá 10610^6.
Output:
  • In ra string {Tên_xâu}; 23 nếu như xâu ss là xâu đối xứng, ngược lại in ra string {Tên_xâu}; 24.
Sample Input:
char c = s[2];

5

Sample Output:
char c = s[2];

6

### Ý tưởng

Cách làm dễ nhất là sử dụng một xâu s1,s\_1, lưu các kí tự của xâu ss theo chiều ngược lại, rồi so sánh hai xâu. Cách làm này không phải một cách hay, vì phép cộng xâu trong C++ sẽ có độ phức tạp bằng độ dài của xâu mới, ngoài ra phép so sánh hai xâu cuối cùng cũng sẽ có độ phức tạp bằng đúng độ dài xâu. Vì thế, cách này chưa tối ưu.

Gọi nn là độ dài của xâu kí tự và coi rằng đánh số các kí tự trong xâu từ vị trí 00 tới vị trí n−1n - 1. Ta nhận xét thấy, nếu một xâu là đối xứng, thì cặp kí tự thứ ii và n−i−1n - i - 1 sẽ giống nhau. Vì thế, chỉ cần sử dụng một vòng lặp duyệt ii từ 00 tới (⌊n2⌋−1)\\left(\\left\\lfloor{\\frac{n}{2}} \\right\\rfloor - 1\\right) rồi kiểm tra cặp kí tự ii và n−i−1n - i - 1 có giống nhau hay không, nếu tồn tại một cặp khác nhau thì kết luận luôn xâu không phải đối xứng.

Bằng cách này, chúng ta giảm được số lần lặp xuống chỉ còn tối đa ⌊n2⌋\\left\\lfloor{\\frac{n}{2}} \\right\\rfloor lần.

### Cài đặt
char c = s[2];

7

## 2\. Chuẩn hóa xâu

### Đề bài

Cho một xâu kí tự ss chỉ gồm các chữ cái latin in thường và in hoa cùng các dấu cách. Một xâu được gọi là chuẩn hóa nếu như nó thỏa mãn các điều kiện sau:

  • Không bao gồm các dấu cách thừa ở đầu và cuối xâu.
  • Gồm nhiều từ, mỗi từ bắt đầu bằng một chữ cái in hoa và theo sau là các chữ cái in thường.
  • Giữa các từ phân tách nhau bằng đúng một dấu cách.
Yêu cầu: Hãy chuẩn hóa xâu ss và đưa ra kết quả? Input:
  • Một dòng duy nhất chứa xâu kí tự ss chỉ bao gồm các chữ cái latin và dấu cách.
Ràng buộc:
  • Độ dài xâu ss không vượt quá 10001000.
Output:
  • In ra xâu ss sau khi đã chuẩn hóa.
Sample Input:
char c = s[2];

8

Sample Output:
char c = s[2];

9

### Ý tưởng

Cách làm hoàn toàn đơn giản như sau:

  • Đầu tiên xóa các dấu cách thừa ở đầu và cuối xâu, sử dụng hàm string {Tên_xâu}; 13 của thư viện cin >> {Tên_biến_xâu}; // Nhập vào một biến xâu. cout << {Tên_biến_xâu}; // Viết ra một biến xâu. 5.
  • Sau đó duyệt qua các kí tự của xâu s,s, lần lượt xử lý các trường hợp kí tự đó là chữ thường, chữ hoa hay dấu cách.
    • Nếu là chữ thường mà đứng ở đầu một từ thì phải viết hoa nó lên, còn là chữ hoa mà không đứng đầu một từ thì viết thường nó.
    • Nếu là dấu cách thì ta bỏ qua không quan tâm tới nó.
    • Nếu kí tự đó là kí tự đầu tiên của mỗi từ thì in thêm ra một dấu cách.
    • Cuối cùng in ra kí tự vừa xử lý.
Tuy nhiên, cần lưu ý trong bài này, xâu nhập vào có dấu cách, vì thế ta cần sử dụng

include

include

using namespace std; int main() {

string name;
cin >> name;
cout << "Tên vừa nhập là: " << name;
return 0;
}

`

6 để nhập xâu.