#c

C++字符数组

从基础到竞赛的字符处理技巧

#c
@鞠大龙
魅客科创中心

CONTENTS

目录

1. 字符类型基础

1.1 char类型回顾

  • char的本质

    • 一个字节(8位)的整数类型
    • 表示范围:-128到127或0到255
    • 通常用于存储ASCII字符
  • 字符常量

    char c1 = 'A';    // 字符形式
    char c2 = 65;     // 数字形式(ASCII码)
    char c3 = '\n';   // 转义字符
    

1.2 ASCII码表

常用ASCII码

  • 大写字母:A-Z (65-90)
  • 小写字母:a-z (97-122)
  • 数字:0-9 (48-57)
  • 空格:32
  • 换行:\n (10)
  • 制表符:\t (9)

ASCII码规律

// 字母间转换
'a' - 'A' == 32  // 大小写差值
'b' - 'a' == 1   // 相邻字母差值

// 数字字符转整数
'5' - '0' == 5   // 字符转数字

1.3 字符与整数转换

// 字符与ASCII码转换
char ch = 'A';
int ascii = ch;          // 字符转ASCII: 65
char back = (char)ascii; // ASCII转字符: 'A'

// 大小写转换
char upper = 'a' - 32;   // 小写转大写: 'A'
char lower = 'A' + 32;   // 大写转小写: 'a'

// 数字字符转换
char digit = '7';
int num = digit - '0';   // 字符转数字: 7
char back_digit = num + '0'; // 数字转字符: '7'

2. 字符数组基础

2.1 定义和初始化

// 定义方式
char str1[100];           // 未初始化
char str2[100] = "";     // 空字符串
char str3[] = "Hello";   // 自动计算长度
char str4[6] = "Hello";  // 指定长度(需要考虑\0)

// 注意事项
char str5[5] = "Hello";  // 错误!没有空间存储\0
char str6[] = {'H','i'}; // 危险!没有\0结束符

2.2 结束符'\0'

结束符特点

  • ASCII码值为0
  • 标志字符串结束
  • 不计入字符串长度
  • 必须预留存储空间

错误示例

char s[5] = "Hello"; // 错误
// 需要6个字符空间:
// H e l l o \0

正确使用

char s[6] = "Hello";
// 内存布局:
// s[0] s[1] s[2] s[3] s[4] s[5]
//  H    e    l    l    o   \0

// 遍历到\0为止
int i = 0;
while(s[i] != '\0'){
    cout << s[i];
    i++;
}

2.3 输入输出

char str[100];

// 输入方式
cin >> str;          // 遇空格停止
cin.getline(str, 100); // 读取整行
gets(str);           // 读取整行(不推荐)

// 输出方式
cout << str;         // 输出到\0为止
puts(str);          // 输出并换行

// 注意事项
char s[5];
cin >> s;           // 危险!可能缓冲区溢出
cin.getline(s, 5);  // 安全,限制输入长度

3. 基本操作

3.1 长度计算

// 手动计算长度
char s[] = "Hello";
int len = 0;
while(s[len] != '\0') len++;

// 使用循环遍历
for(int i = 0; s[i]; i++){  // s[i]为0时停止
    // 处理s[i]
}

// 常见错误
char s1[] = {'H','i'};   // 没有\0,长度计算不准确
char s2[5] = "Hello";    // 数组越界

3.2 字符查找

查找单个字符

char s[] = "Hello";
char target = 'l';

// 查找第一次出现
int pos = -1;
for(int i = 0; s[i]; i++){
    if(s[i] == target){
        pos = i;
        break;
    }
}

// 查找最后一次出现
pos = -1;
for(int i = 0; s[i]; i++){
    if(s[i] == target){
        pos = i;
    }
}

统计出现次数

char s[] = "Hello";
char target = 'l';

int count = 0;
for(int i = 0; s[i]; i++){
    if(s[i] == target){
        count++;
    }
}

// 使用数组统计所有字符
int freq[128] = {0};  // ASCII字符频率
for(int i = 0; s[i]; i++){
    freq[s[i]]++;
}

3.3 字符替换

// 替换单个字符
char s[] = "Hello";
char old_ch = 'l';
char new_ch = 'w';

for(int i = 0; s[i]; i++){
    if(s[i] == old_ch){
        s[i] = new_ch;
    }
}
// 结果: "Hewwo"

// 大小写转换
for(int i = 0; s[i]; i++){
    if(s[i] >= 'a' && s[i] <= 'z'){
        s[i] = s[i] - 32;  // 转大写
    }
}
// 结果: "HELLO"

4. 常见应用

4.1 字符串比较

// 比较两个字符串
char s1[] = "Hello";
char s2[] = "Hello";

bool isEqual = true;
for(int i = 0; s1[i] || s2[i]; i++){
    if(s1[i] != s2[i]){
        isEqual = false;
        break;
    }
}

// 字典序比较
int compare(char s1[], char s2[]){
    int i = 0;
    while(s1[i] && s2[i]){
        if(s1[i] != s2[i]){
            return s1[i] - s2[i];
        }
        i++;
    }
    return s1[i] - s2[i];  // 处理不等长情况
}

4.2 回文串判断

// 判断回文串
bool isPalindrome(char s[]){
    int len = 0;
    while(s[len]) len++;  // 计算长度
    
    for(int i = 0; i < len/2; i++){
        if(s[i] != s[len-1-i]){
            return false;
        }
    }
    return true;
}

// 忽略大小写的回文判断
bool isPalindromeIgnoreCase(char s[]){
    int len = 0;
    while(s[len]) len++;
    
    for(int i = 0; i < len/2; i++){
        char c1 = s[i], c2 = s[len-1-i];
        // 转换为小写比较
        if(c1 >= 'A' && c1 <= 'Z') c1 += 32;
        if(c2 >= 'A' && c2 <= 'Z') c2 += 32;
        if(c1 != c2) return false;
    }
    return true;
}

4.3 子串查找

// 查找子串
int findSubstring(char s[], char sub[]){
    int i = 0, j;
    while(s[i]){
        j = 0;
        while(sub[j] && s[i+j] == sub[j]){
            j++;
        }
        if(!sub[j]) return i;  // 找到子串
        i++;
    }
    return -1;  // 未找到
}

// 示例使用
char text[] = "Hello World";
char pattern[] = "World";
int pos = findSubstring(text, pattern);
// pos = 6

5. 实战练习

5.1 基础题目

1. 字符统计 (HDU 2017)

  • 统计字符串中数字的个数
char s[100];
cin.getline(s, 100);
int count = 0;
for(int i = 0; s[i]; i++){
    if(s[i] >= '0' && s[i] <= '9'){
        count++;
    }
}
cout << count << endl;

2. 大小写转换 (HDU 2026)

  • 将每个单词的首字母改成大写
char s[100];
cin.getline(s, 100);
if(s[0] >= 'a' && s[0] <= 'z'){
    s[0] -= 32;
}
for(int i = 1; s[i]; i++){
    if(s[i-1] == ' ' && 
       s[i] >= 'a' && s[i] <= 'z'){
        s[i] -= 32;
    }
}
cout << s << endl;

5.2 进阶题目

1. 回文串 (POJ 1488)

  • 判断字符串是否为回文串
  • 忽略空格和大小写
char s[1000], t[1000];
int len = 0;
cin.getline(s, 1000);
// 预处理:去除空格,转小写
for(int i = 0; s[i]; i++){
    if(s[i] != ' '){
        if(s[i] >= 'A' && s[i] <= 'Z'){
            t[len++] = s[i] + 32;
        } else {
            t[len++] = s[i];
        }
    }
}
t[len] = '\0';

2. 字符串匹配 (HDU 2087)

  • 计算模式串在主串中出现的次数
char s[1001], p[1001];
cin >> s >> p;
int count = 0, pos = 0;
while(s[pos]){
    bool found = true;
    for(int i = 0; p[i]; i++){
        if(!s[pos+i] || s[pos+i] != p[i]){
            found = false;
            break;
        }
    }
    if(found){
        count++;
        int i = 0;
        while(p[i]) i++;
        pos += i;
    } else {
        pos++;
    }
}
cout << count << endl;

5.3 竞赛真题

  1. USACO 2016 Dec Bronze

    • Block Game
    • 统计字符出现次数
    • 考察字符频率统计
  2. CSP-J 2019

  3. POJ 3461

    • Oulipo
    • 模式串匹配
    • 考察子串查找算法

6. 总结

6.1 关键要点

  • 字符数组以'\0'结尾
  • 注意数组边界
  • 常用ASCII码转换
  • 重视输入安全

6.2 常见错误

  1. 忘记预留'\0'空间
  2. 数组越界访问
  3. 未初始化数组
  4. 忽略输入缓冲区溢出

6.3 竞赛技巧

  1. 使用频率数组统计字符
  2. 灵活运用ASCII码运算
  3. 注意特殊字符处理
  4. 考虑边界测试用例

#c

C++字符数组

#c

感谢学习!
如有疑问,请联系:
judal@xmaker.org
魅客科创中心

这个讲义使用了Awesome Marp主题的蓝色风格。 内容针对竞赛选手设计,注重基础概念和实际应用。