20251120 StringOperation | 字符串操作

发布于 10 天前  156 次阅读


题目

编写一个程序,输入两个字符串s1s2,使用 指针操作 构造一个新的字符串merged,具体按以下规则将两者的字符按ASCII值升序交替合并:

  1. 每次从两个字符串当前位置各取一个字符,将ASCII值较小的字符先放入新串。
  2. 若两个字符相同,则先放入来自s1的字符。
  3. 当其中一个字符串结束后,将另一个字符串的剩余部分追加到结果末尾。
  4. 如果即将插入的字符与结果串末尾的字符相同,则跳过(不重复插入)。

要求

  1. 须使用 指针操作(如*p1, *(p2+1)等),不得使用下标访问(如s1[i]s2[j])。
  2. 可以通过动态内存分配为合并结果申请空间,最后释放动态内存。
  3. 不得使用字符串函数strcat()strcpy()strncat()等拼接函数。
  4. 输入字符串中不含空格,长度不超过20

输入输出格式:
输入:

s1
s2 // s1 和 s2 均为一行字符串。

输出:

merged // 合并后的字符串

示例:
输入:

abbc // s1
ab // s2

输出:

abc

思路

  1. 首先,需要读取两个字符串s1s2,我们用两个char数组(即字符串)来存储它们,方便进行后续操作。
    char数组声明时,需要指定数组大小,即字符串长度。这里为多少合适呢?由于字符串长度不超过20,我们可以声明大小为21的 数组,以容纳字符串末尾的空字符\0

    char s1[21], s2[21];
  2. 接着,我们需要对两个字符串进行合并。我们预计将合并的结果存储在一个新的字符串merged中。
    同样我们需要指定数组大小,这里我们可以声明大小为41的数组(20 + 20 + 1),以容纳两个字符串的最大长度和末尾的空字符\0

    char merged[41];
  3. 接下来我们要对合并这一逻辑进行处理。根据给出的样例说明,我们给出的样例,具体逻辑由如下方式实现:
    1. 样例:
      s1 = "abbc", s2 = "ab"
      merged = "abc"
    2. 逻辑:
      • 考虑两个箭头,分别指向我们当前处理的s1s2中的字符位置。最开始它们均指向[0]位置
        s1: abbc
            ↑ (位置[0])
        s2: ab
            ↑ (位置[0])
        merged: (空)
      • 每次进行以下操作:
        • 比较两个箭头所指向的字符的ASCII值。
          • 如果值不同,则将ASCII值较小的字符准备插入到merged中。
          • 如果值相同,则将s1中的字符准备插入到merged中。
        • 如果merged中最后一个字符与准备插入的字符相同,则不进行此次插入,否则将其插入到merged中。
        • 对于指向准备插入的字符的箭头(可能是s1的箭头,可能是s2的箭头),如果还可以向后移动,则向后移动一位。
        • 重复上述操作,直到其中一个字符串的箭头到达字符串末尾。
      • 当其中一个字符串处理完后,将另一个字符串的剩余部分依次插入到merged中,插入时同样需要检查是否与merged末尾字符相同,避免重复插入。
    3. 过程:
      初始状态:
      s1: abbc
          ↑
      s2: ab
          ↑
      merged: (空)
      第一次比较:
      s1[0] = 'a', s2[0] = 'a' (相同,取s1)
      merged: a
      s1: abbc
           ↑
      s2: ab
          ↑
      第二次比较:
      s1[1] = 'b', s2[0] = 'a' (s2较小,但merged末尾'a'与's2[0]'相同,跳过)
      merged: a
      s1: abbc
           ↑
      s2: ab
           ↑
      第三次比较:
      s1[1] = 'b', s2[1] = 'b' (相同,取s1)
      merged: a b
      s1: abbc
            ↑
      s2: ab
           ↑
      第四次比较:
      s1[2] = 'b', s2[1] = 'b' (相同,取s1,但merged末尾'b'与's1[2]'相同,跳过)
      merged: a b
      s1: abbc
             ↑
      s2: ab
           ↑
      第五次比较:
      s1[3] = 'c', s2[1] = 'b' (s2较小,但merged末尾'b'与's2[1]'相同,跳过)
      merged: a b
      s1: abbc
             ↑
      s2: ab
            ↑
      s2已处理完,处理s1剩余部分:merged末尾'b'与's1[3]'不同,插入'c'
      merged: a b c
      最终结果:
      merged: abc

      代码实现

      这里叙述的逻辑仍然略显冗长,我们总结一下:

  4. 判断两个字符串的当前字符,决定插入哪个字符到merged中。
    • 如果某个字符串已经处理完,则直接插入另一个字符串的字符。(最高优先级
    • 如果两个字符相同,插入s1的字符。
    • 如果两个字符不同,插入ASCII值较小的字符。
  5. 如果即将插入的字符与结果串末尾的字符相同,则跳过(不重复插入)。
  6. 如果可以移动箭头,则移动对应字符串的箭头。
  7. 重复上述步骤,直到两个字符串都处理完。

Remark: 怎么判断“已经处理完?”,对于字符串,我们可以利用末尾的空字符\0来判断。如果当前字符是\0,说明已经处理完了。

#include <stdio.h>

int main() {
    char s1[21], s2[21];
    char merged[41];

    // 读取输入字符串
    scanf("%s", s1);
    scanf("%s", s2);

    int index_s1 = 0, index_s2 = 0, index_merged = 0; // 用于追踪各字符串的当前位置

    while (*(s1 + index_s1) != '\0' || *(s2 + index_s2) != '\0') {
        // while 循环条件:只要有一个字符串未处理完,就继续

        char char_to_add;
        // 决定要插入哪个字符
        if (*(s1 + index_s1) == '\0') {
            // s1 已处理完,取 s2 的字符
            char_to_add = *(s2 + index_s2);
            index_s2++;
        } else if (*(s2 + index_s2) == '\0') {
            // s2 已处理完,取 s1 的字符
            char_to_add = *(s1 + index_s1);
            index_s1++;
        } else if (*(s1 + index_s1) <= *(s2 + index_s2)) {
            // s1 的字符较小或相等,取 s1 的字符
            char_to_add = *(s1 + index_s1);
            index_s1++;
        } else {
            // s2 的字符较小,取 s2 的字符
            char_to_add = *(s2 + index_s2);
            index_s2++;
        }

        // 检查是否与 merged 末尾字符相同,避免重复插入
        if (index_merged == 0 || merged[index_merged - 1] != char_to_add) { 
            // 判断 index_merged == 0 是为了处理 merged 为空的情况
            merged[index_merged] = char_to_add;
            index_merged++;
        }
    }

    // 结束字符串
    merged[index_merged] = '\0';

    // 输出结果
    printf("%s\n", merged);

    return 0;
}

Remarks

  1. 代码中使用了指针操作来访问字符串中的字符,例如*(s1 + index_s1),而不是使用下标访问s1[index_s1]
  2. 动态内存分配在这里没有使用,因为题目中并没有强制要求必须使用动态内存分配。不过,如果需要,对于声明merged数组,可以使用malloc来动态分配内存,并在最后使用free释放内存。
    原来代码中声明merged数组的部分:

    char merged[41];

    可以改为:

    char* merged = (char*)malloc(41 * sizeof(char));

    并在程序结束前添加:

    free(merged);
  3. 几个需要注意的地方:
    1. while循环的条件是只要有一个字符串未处理完就继续循环,这样可以确保所有字符都被处理。
    2. 在插入字符到merged之前,必须检查是否与末尾字符相同,以避免重复插入。
      而这里存在一个特别的情况:当merged为空时,直接插入第一个字符即可,因此需要额外判断index_merged == 0
    3. 最后需要在merged字符串的末尾添加空字符\0,以确保它是一个合法的字符串。

这里是 /* Huajidawang */ 的个人主页