ACM/ICPC 語法學習筆記—字符串

定義

字符串常量是由一對雙括號引起的字符序列。例如"C language"、"student"、"123"等都是合法的字符串常量。

字符串常量和字符常量的區別:

  1. 字符常量由單引號括起來,字符串常量由雙括號括起來。 'C' "China"

  2. 字符常量只能是單個字符,字符串常量則可以含零個或多個字符。

  3. 可以把一個字符常量賦予一個字符變量,但不能把一個字符串常量賦予一個字符變量。在C語言中沒有相應的字符串變量,但是可以用一個字符數組來存放一個字符串常量。Str[6] = "China";

  4. 字符常量佔一個字節的內存空間。字符串不像其他數據類型具有固定的長度,不用字符串是不等長的,因此,字符串的存儲不光需要存儲其起始位置,還應該記載其結束位置。字符串常量佔的內存字節數等於字符串中字符數加1,增加的一個字節中存放字符'\0'(ASCII碼爲0),這是字符串結束的標誌。如字符串常量"China"中實際上有六個字符,分別是'C''h''i''n''a''\0'

    注:字符常量'A'和字符串常量"A"雖然都只有一個字符,但是在內存中的情況不同。

    如以下代碼程序:

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        char a = 'C';
        string b = "C";
        printf("字符'c'的size:%d\n", sizeof(a));
        printf("字符串%\"C\"的size:%d\n", sizeof(b));
    }
    

    代碼運行的結果是:

    字符'c'的size:1
    字符串%"C"的size:32
    

字符數組與字符串

在C語言中沒有專門的字符串變量,通常用一個字符數組來存放一個字符串。(注:在C++中對於字符串有類string可以通過引用#include <string>來直接使用string新建字符串變量。具體內容在本文最後進行講解)字符串是以\0作爲串的結束符。因此把一個字符串存入一個數組時結束符'\0'也需要存入數組,並以此作爲該字符串結束的標誌。有了\0標誌之後,就不必再用字符數組的長度來判斷字符串的長度了。

C語言允許用字符串的方式對字符數組做初始化賦值,但是與用字符初始化是有區別的。

例如

char ch1[] = {'C','h','i','n','a'};
//數組ch1的大小爲5,包含1個大寫字母,4個小寫字母
char ch2[] = "China";
char ch3[] = {"China"};
//以上兩句話是等效的,數組ch2、ch3的大小均爲6,包含1個大寫字母,4個小寫字母,和一個\0

對於字符數組可以有兩種使用方式:

  • 當做單個字符看待。(ch[0]ch[1]等)

  • 當做一個字符串看待,使用數組名訪問整個數組,直接操作字符串中的個所有字符。可以使用printfscanf函數一次性輸入輸出一個字符數組中的字符串,而不必使用循環語句逐個地輸入輸出每個字符。

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        char ch[6] = "China";
        //按照字符逐個輸出
        for (int i = 0; i < 6; i ++ ) {
            printf("%c", ch[i]);
        }
        printf("\n");
        //按照字符串整體輸出
        printf("%s", ch);
        return 0;
    }
    

    特別的,在定義字符串的時候,可以省略掉字符數組的大小,如以下兩種情況在編譯過程中是完全等價的:

    ch[] = "China";
    ch[6] = "China";
    

兩種方式輸入字符串:

同字符串的輸出一樣,輸入一個字符串有兩種方式。

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch1[6], ch2[6];
    //按照字符數組逐個讀入
    for (int i = 0; i <= 5; i ++ ) {
        scanf("%c", &ch1[i]);
    }
    //按照字符串整體讀入
    scanf("%s", ch2);

    printf("Ans1:%s\n", ch1);
    printf("Ans2:%s\n", ch2);
}

當輸入爲China China時,輸出爲:

Ans1:China 
Ans2:China

例題1:[洛谷 P5733 【深基6.例1】自動修正](P5733 【深基6.例1】自動修正 - 洛谷 | 計算機科學教育新生態 (luogu.com.cn))

例題2:洛谷 P1914 小書童——凱撒密碼

例題3:洛谷 P1125 笨小猴

C語言中字符串函數

C語言提供了豐富的字符串處理函數,大致可分爲字符串的輸入輸出、合併、修改、比較、轉換、複製及搜索幾類。使用這些函數可大大減輕變成的負擔。用於輸入輸出的字符串函數,在食用前應包含頭文件stdio.h,使用其他字符串函數則應包含頭文件string.h

字符串輸出函數puts

  • 規格說明:int put(const char * pc);

  • 功能描述:把字符指針指定的字符串輸出到標準輸出流中,空字符不輸出,但是輸出換行符及空格等分隔符。

  • 函數參數:pc可以是指針變量,也可以是字符數組名。

  • 函數返回值:若出現些錯誤,則返回EOF(-1);否則返回非負數。

例如:

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch[] = {"China"};
    puts(ch);
    return 0;
}

運行結果爲:China

注:puts()函數完全可以由printf()函數取代。當需要按一定格式輸出時,通常使用printf()函數。

字符串讀入函數gets

  • 規格說明:char * gets(char * pc);

  • 功能描述:從標準輸入流讀字符到字符指針指向的字符數組中,直至遇到換行符或輸入文檔結束符,並且丟棄換行符或結束符,然後在字符數組中添加一個空字符。

  • 函數參數:pc必須是字符數組名或指向字符數組的指針變量。不可以使用指針變量指向字符串常量,因爲常量空間不允許改變。

  • 函數返回值:若成功,返回pc1;若直接遇見換行符或結束符,pc所指數組不變,返回空指針,若讀入長度超出數組長度,數組改變,會出現內存寫問題(超出數組長度)。

例如:

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch[11];
    printf("input string:\n");
    gets(ch);
    printf("output string:\n");
    puts(ch);
    return 0;
}

運行結果爲:

input string:
Love China
output string:
Love China

將字符串讀入字符數組還可以使用gets()函數,但是由於存在字符數組越界的風險,已經不再建議使用,新的C++11標準更是刪除了這個函數,而輸出一個字符串還可以使用puts(),同時會自動輸出換行,這倒是還能使用。

測字符串長度函數strlen

  • 規格說明:int strlen(const char * pc);

  • 功能描述:計算pc所指向的字符串的長度。

  • 函數參數:pc可以是指針變量,也可以是字符數組名。

  • 函數返回值:返回pc指向數組中儲存字符串的長度,即第一個\0之前的有效字符個數。

例如:

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch1[] = {'I',' ','l','o','v','e','\0','y','o','u','\0'};
    char ch2[] = {'I',' ','l','o','v','e',' ','y','o','u','\0'};
    int len1 = strlen(ch1);
    int len2 = strlen(ch2);
    printf("%d\n", len1);
    printf("%d", len2);
    return 0;
}

運行結果爲:

6
10

字符串連接函數strcat

  • 規格說明:char * strcat(char * pc1, const char * pc2);

  • 功能描述:把字符指針pc1所指向的字符數組的末尾添加pc2所指向的字符串(包括結尾的空字符在內)的副本,即用pc2所指向的字符串的第一個字符覆蓋pc1所指向的字符串末尾的空字符

  • 函數參數:pc1必須是字符數組名或只想字符數組的指針變量,不可以使用指針變量指向字符串常量。pc2可以是指針變量,也可以是字符數組名。

  • 函數返回值:返回pc1,若連接後的pc1長度超出pc1指向數組長度,會出現內存寫問題(超出數組長度)。

例如:

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch[20] = {"I love"};
    char ch2[7] = " China";
    strcat(ch, ch2);
    printf("%s", ch);
    return 0;
}

運行結果爲:I love China

字符串複製函數strcpy

  • 規格說明:char * strcpy(char * pc1, const char *pc2);

  • 功能描述:把字符指針pc2所指向的字符串(包括結尾的空字符在內)複製到pc1所指向的字符數組中。

  • 函數參數:pc1必須是字符數組名或只想字符數組的指針變量,不可以使用指針變量指向字符串常量。pc2可以是指針變量,也可以是字符數組名。

  • 函數返回值:返回pc1,若複製後的pc1長度超出pc1指向數組的長度,會出現內存寫問題(超出數組長度)。

例如:

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch1[20] = "I love ";
    char ch2[20] = "China";
    strcpy(ch1, ch2);
    printf("%s", ch1);
    return 0;
}

運行結果爲:China

字符串比較函數strcmp

  • 規格說明:int strcmp(cons char *pc1, const char *pc2);
  • 功能描述:按照ASCII碼順序比較字符指針pc1pc2所指向的串的大小。
  • 函數參數:pc1pc2可以是指針變量,也可以是字符數組名
  • 函數返回值:
    • pc1所指向的字符串 == pc2指向的字符串,返回值0;
    • pc1所指向的字符串 > pc2`指向的字符串,返回值大於0;
    • pc1所指向的字符串 < pc2指向的字符串,返回值小於0;

例如:

#include <bits/stdc++.h>
using namespace std;
int main() {
    char * pc1 = "abc";
    char * pc2 = "ABC";
    char * pc3 = "abc";
    char * pc4 = "abd";
    char * pc5 = "ab";
    printf("cmp pc1 ans pc2:%d\n", strcmp(pc1, pc2));
    printf("cmp pc1 ans pc3:%d\n", strcmp(pc1, pc3));
    printf("cmp pc1 ans pc4:%d\n", strcmp(pc1, pc4));
    printf("cmp pc1 ans pc5:%d\n", strcmp(pc1, pc5));
    return 0;
}

運行結果爲:

cmp pc1 ans pc2:1
cmp pc1 ans pc3:0
cmp pc1 ans pc4:-1
cmp pc1 ans pc5:1

字符串查找函數strstr

  • 規格說明:char * strstr(const char * pc1, const char * pc2);
  • 功能描述:查找字符指針pc1所指向的字符串中第一次出現pc2所指向字符串(不包括空字符)的地址。
  • 函數參數:pc1pc2可以是指針變量,也可以是字符數組名。
  • 函數返回值:返回字符指針pc1所指向字符串中第一次出現pc2所指向字符串(包括空字符)的地址。若不出現返回空指針。

例如:

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch[20] = "I love China.";
    char *pc = "China";
    char *tmp = strstr(ch, pc);
    int pos = strlen(ch) + 1 - strlen(tmp);
    printf("%d", pos);
    return 0;
}

運行結果爲:8

C++ string類型字符串

使用C語言風格的字符數組有諸多不便,比如不能彈性變化長度,不能直接賦值或者複製,也有數組越界的風險。在C++中提供了一些更好的工具——標準模板庫(Standard Template Library,STL),將很多有用的功能進行了封裝,開箱即用,而不需要另外重新開發這些功能。STL包括各類容器(如隊列、棧等)、算法(如排序)和其他的一些功能。現在,將要使用string數據類型來處理字符串問題。

像前面所說的,C語言中沒有字符串這種數據類型,而在C++中我們可以直接使用string來定義一個字符串。

例題4:洛谷 P5015 標題統計

像上面這道例題,可以不使用字符數組,而是使用一種新的“數據類型”——string。一個string類型的變量可以用來儲存一個字符串,並且可以激昂這個字符串當做一個整體進行處理——可以對string進行賦值、拼接、裁切等,而字符數組畢竟是個數組,做到這些相對麻煩。

輸入時使用cin語句,不斷讀入字符串。當發現讀入文件讀完後,會直接中斷while循環。

這裏的s可以理解爲一個“加強版”的字符數組,可以使用s.length()來直接查詢字符串s的長度,也可以和字符數組一樣使用s[0]來查詢這個字符串最開頭的字符是什麼。

而且string類型的字符串可以直接拿來賦值、拼接操作,比如s = s + s就是將兩個字符串s拼接在一起,其結果賦值回s的意思。

例題5:洛谷 P5734 【深基6.例6】文字處理軟件

這道題用到了string類型的常用處理方法:

  1. string s定義一個名字爲s的字符串變量。
  2. s+= str或者s.append(str)在字符串s後面拼接字符串str。
  3. s<str比較字符串s的字典序是否在str的字典序之前。
  4. s.size()s.length()得到字符串s的長度。
  5. s.substr(pos, len)截取字符串s,從第pos個位置開始len個字符,並返回這個字符串。
  6. s.insert(pos, len)在字符串s的第pos個字符之前,插入字符串str,並返回這個字符串。
  7. s.find(str, [pos])在字符串s中從第pos個字符開始尋找str,並返回位置,如果找不到返回-1。pos可以省略,默認爲0。

例題6:洛谷 P1308 統計單詞數

代碼中使用了getline()函數,可以講完整的一行的輸入數據讀入到字符串中,無論這一行當中是否有空格。使用方法是getline(cin, 字符串名稱)

其他類別函數

fgets()函數

fgets()函數用來讀入一行字符串,並存入字符數組中,空格也一起存下了。由於gets()函數因爲存在可能溢出的風險而不是用。fgets(s, sizeof(s), stdin)這條語句中制定了字符數組的最大讀入數量,因此是安全的。

sscanf()函數和sprintf()函數

sscanf()函數,可以從已經存儲下來的字符串中讀入信息。sprintf()可以將信息輸出到字符串當中。比如sscanf(s, "%d", a);就可以從s字符串中讀入一個整數a。同理sprintf(s, %d, a);可以將一個int類型的數a輸出到字符串s中。

例題7:洛谷 P1957 口算練習題

習題

注:例題和加粗習題爲必做題

洛谷 P1765 手機

洛谷 P3741 honoka的鍵盤

洛谷 P1321 單詞覆蓋還原

洛谷 P1553 數字反轉(升級版)

洛谷 P1603 斯諾登的密碼

洛谷 P1200 你的飛碟在這兒Your Ride Is Here

洛谷 P1597 語句解析

洛谷 P1598 垂直柱狀圖 【儘量做】

例題答案

例題1:[洛谷 P5733 【深基6.例1】自動修正](P5733 【深基6.例1】自動修正 - 洛谷 | 計算機科學教育新生態 (luogu.com.cn))

讀入字符串,對每一個字符進行分析,如果當前字符ch[i]>='a'&&ch[i]<='z'證明這是一個小寫字母,把他變成大寫字母即可。

#include <bits/stdc++.h>
using namespace std;
int main() {
    char ch[110];
    scanf("%s", ch);
    for (int i = 1; ch[i] != '\0'; i ++ ) {
        if (ch[i] >= 'a' && ch[i] <= 'z') {
            ch[i] += 'A' - 'a';
        }
    }
    printf("%s", ch);
    return 0;
}

例題2:洛谷 P1914 小書童——凱撒密碼

讀入字符串,把其中每一個字符加上\(n\),特別注意的,在當前字符加上\(n\)之後,可能會超過'a'~'z'的範圍,需要特殊處理。

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    char ch[60];
    scanf("%d", &n);
    scanf("%s", ch);
    for (int i = 0 ; ch[i] != '\0'; i ++ ) {
        ch[i] = (ch[i] - 'a' + n) % 26 + 'a';
    }
    for (int i = 0; ch[i] != '\0'; i ++ ) {
        printf("%c", ch[i]);
    }
    return 0;
}

例題3:洛谷 P1125 笨小猴

讀入字符串,統計該字符串中出現最多的字母的次數和出現最少的字母的次數,進行相減,判斷結果是否爲質數,如果是質數則輸出Lucky Word與這個質數,不是則輸出No Answer0

#include <bits/stdc++.h>
using namespace std;
#define inf 2147483647
int main() {
    char ch[110];
    int maxx = 0, minn = inf, cnt[26] = {0};
    scanf("%s", ch);
    for (int i = 0; ch[i] != '\0'; i ++ ) {
        int now = ch[i] - 'a';//獲取當前字母是26箇中的第幾個(0~25)
        cnt[now] ++ ;
    }
    for (int i = 0; i < 26; i ++ ) {
        if (cnt[i] == 0) {
            continue;
            //判斷26個字母中第i(0~25)個是否出現,不出現則不統計
        }
        if (cnt[i] >= maxx) {
            maxx = cnt[i];
        }
        if (cnt[i] <= minn) {
            minn = cnt[i];
        }
        //找到這個單詞中出現最多字母出現的次數和出現最少字母出現的次數
    }
    //下面爲常規判斷質數的方法
    if (maxx - minn == 0 || maxx - minn == 1) {
        printf("No Answer\n0");
        return 0;
    }
    for (int i = 2; i * i <= maxx - minn; i ++ ) {
        if ((maxx - minn) % i == 0) {
            printf("No Answer\n0");
            return 0;
        }
    }
    printf("Lucky Word\n%d", maxx - minn);
    return 0;
}

例題4:洛谷 P5015 標題統計

這裏使用了string類型的變量,因爲讀入字符串會忽略前面的空格,並且讀到分隔符(空格或者換行)時停止,所以可以一直讀入字符串,每次讀入一個字符串,把其長度加入答案中即可。

#include <bits/stdc++.h>
using namespace std;
int main() {
    string s;
    int cnt = 0;
    while (cin >> s) {
        cnt += s.length();
    }
    printf("%d", cnt);
    return 0;
}

例題5:洛谷 P5734 【深基6.例6】文字處理軟件

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n;
    scanf("%d", &n);
    string s;
    cin >> s;
    for (int i = 1; i <= n; i ++ ) {
        int opt, a, b;
        string tmp;
        scanf("%d", &opt);
        if (opt == 1) {
            cin >> tmp;
            s += tmp;
            cout << s << endl;
        }
        if (opt == 2) {
            scanf("%d%d", &a, &b);
            s = s.substr(a, b);
            cout << s << endl;
        }
        if (opt == 3) {
            scanf("%d", &a);
            cin >> tmp;
            s.insert(a, tmp);
            cout << s << endl;
        }
        if (opt == 4) {
            cin >> tmp;
            int ans = s.find(tmp);
            cout << ans << endl;
        }
    }
    return 0;
}

例題6:洛谷 P1308 統計單詞數

首先將讀入的單詞和句子中的每個字母都換成小寫字母(大寫字母也可)。然後我們發現,如果要查找的單詞是to時,若句子爲I like to walk to Toshiba Company.的時候,將單詞Toshiba中的to也參與了統計。那麼我們這裏利用一個小處理,把要查找的單詞前後各加一個空格,輸入的句子最前邊和最後邊也加一個空格,這樣 ‘ ’+"to"+' '去查找,即可統計單個的單詞數量。

#include <bits/stdc++.h>
using namespace std;
int main() {
    string s, SS;
    getline(cin, s);
    getline(cin,SS);
    for (int i = 0; i < SS.length(); i ++ ) {
        if (SS[i] >= 'A' && SS[i] <= 'Z') {
            SS[i] += 32;
        }
    }
    for (int i = 0; i < s.length(); i ++ ) {
        if (s[i] >= 'A' && s[i] <= 'Z') {
            s[i] += 32;
        }
    }
    s = ' ' + s + ' ';
    SS = ' ' + SS + ' ';
    if (SS.find(s) == -1) {
        cout << -1 << endl;
        return 0;
    }
    int nxt = SS.find(s);
    int pos = nxt;
    int cnt = 0;
    while (nxt != -1) {
        cnt ++ ;
        nxt = SS.find(s, nxt + 1);
    }
    cout << cnt << ' ' << pos << endl;
    return 0;
}

例題7:洛谷 P1957 口算練習題

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, a, b, c;
    char opt, s[20], ans[20];
    scanf("%d\n", &n);
    while(n -- ) {
        fgets(s, sizeof(s), stdin);
        if (s[0] == 'a' || s[0] == 'b' || s[0] == 'c') {
            opt = s[0];
            s[0] = ' ';
        }
        sscanf(s, "%d %d", &a, &b);
        switch(opt) {
            case 'a' : 
                c = a + b;
                sprintf(ans, "%d+%d=%d", a, b, c);
                break;
            case 'b' :
                c = a - b;
                sprintf(ans, "%d-%d=%d", a, b, c);
                break;
            case 'c' :
                c = a * b;
                sprintf(ans, "%d*%d=%d", a, b, c);
        }
        printf("%s\n", ans);
        printf("%d\n", strlen(ans));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章