L1-011 A-B 【保姆级详细讲解】

本题要求你计算A−B。不过麻烦的是,A和B都是字符串 —— 即从字符串A中把字符串B所包含的字符全删掉,剩下的字符组成的就是字符串A−B。

输入格式:

输入在2行中先后给出字符串A和B。两字符串的长度都不超过104,并且保证每个字符串都是由可见的ASCII码和空白字符组成,最后以换行符结束。

输出格式:

在一行中打印出A−B的结果字符串。

输入样例:

I love GPLT!  It's a fun game!
aeiou

输出样例:

I lv GPLT!  It's  fn gm!

目录

输入格式:

输出格式:

输入样例:

输出样例:

标记数组法

完整代码

 变量定义

 读取输入

 标记字符串B中的字符

 过滤并输出字符串A中的字符

示例分析

关键点

 双指针法

完全代码

输入读取

初始化变量

 核心处理逻辑

 示例分析

 字符串删除法(不推荐)

完整代码

详细步骤

 示例演示

 Python解法

完整代码

为什么这样设计?

示例分析


浅说:这道题有4种解法,标记数组法双指针法字符串删除法Python解法,每种解法的思路和步骤小假在下面都做了详细地讲解,方便大家理解~


标记数组法

完整代码

#include<bits/stdc++.h>
using namespace std;
string a, b;
bool v[256]; // 用于标记字符是否在B中出现
int main() {
    getline(cin, a); // 读取字符串A,包括空格
    getline(cin, b); // 读取字符串B,包括空格
    for (char c : b) { // 遍历字符串B的每一个字符
        v[c] = true; // 标记该字符为“需要删除”
    }
    for (char c : a) { // 遍历字符串A的每一个字符
        if (!v[c]) cout << c; // 如果字符没有被标记,则输出
    }
    return 0;
}

 变量定义

#include<bits/stdc++.h>
using namespace std;
string a, b;
bool v[256];

  • string a, b;:定义两个字符串变量ab,分别用于存储输入的字符串A和B。

  • bool v[256];:定义一个布尔数组v,用于标记字符是否在字符串B中出现。ASCII码共有128个字符(扩展ASCII码为256个),这里使用256是为了覆盖所有可能的字符。

 读取输入

getline(cin, a);
getline(cin, b);
  • getline(cin, a);:读取第一行输入,存储到字符串a中。getline可以读取整行,包括空格。

  • getline(cin, b);:读取第二行输入,存储到字符串b中。

 标记字符串B中的字符

for (char c : b) {
    v[c] = true;
}
  • 这是一个范围for循环,遍历字符串b中的每一个字符c

  • v[c] = true;:将字符c对应的ASCII码作为索引,将v数组的该位置设为true,表示该字符需要从a中删除。

 过滤并输出字符串A中的字符

for (char c : a) {
    if (!v[c]) cout << c;
}
  • 遍历字符串a中的每一个字符c

  • if (!v[c]):检查v[c]是否为false,即字符c是否不在字符串b中。

  • 如果v[c]false,说明c不需要被删除,输出该字符。

  • 如果v[c]true,说明cb中出现过,跳过不输出。

示例分析

输入

I love GPLT!  It's a fun game!
aeiou
  • 读取输入

    • a = "I love GPLT! It's a fun game!"

    • b = "aeiou"

  • 标记字符

    • 遍历b中的字符:'a', 'e', 'i', 'o', 'u'。

    • 设置v['a'] = truev['e'] = true, ..., v['u'] = true

  • 过滤并输出

    • 遍历a中的字符:

      • 'I':v['I']false → 输出。

      • ' ':v[' ']false → 输出。

      • 'l':v['l']false → 输出。

      • 'o':v['o']true → 不输出。

      • 'v':v['v']false → 输出。

      • 'e':v['e']true → 不输出。

      • ... 以此类推。

  • 最终输出:
I lv GPLT! It's fn gm!

关键点

  • 字符标记:使用一个布尔数组v来标记哪些字符需要被删除。数组的索引是字符的ASCII码值。

  • 高效过滤:通过遍历字符串a并检查v数组,可以在O(n)时间内完成过滤,其中n是字符串a的长度。

  • 处理所有字符:包括空格和标点符号等,getline可以正确读取这些字符。

 双指针法

完全代码

#include <stdio.h>
#include <string.h>

int main() {
    char A[10001], B[10001];
    scanf("%[^\n]%*c", A);  // 读取整行
    scanf("%[^\n]%*c", B);
    
    int lenA = strlen(A), lenB = strlen(B);
    int p, q, newlenA = lenA;
    
    for (int i = 0; i < lenB; i++) {
        p = q = 0;
        for (q = 0; q < newlenA; q++) {
            if (A[q] != B[i]) {
                A[p++] = A[q];
            }
        }
        newlenA = p;
    }
    
    for (int i = 0; i < newlenA; i++) {
        printf("%c", A[i]);
    }
    
    return 0;
}

输入读取

scanf("%[^\n]%*c", A);  // 读取整行
scanf("%[^\n]%*c", B);
  • %[^\n] 表示读取直到遇到换行符的所有字符

  • %*c 表示读取但不存储换行符(丢弃它)

  • 这样分别读取了两行输入,第一行存入A,第二行存入B

初始化变量

int lenA = strlen(A), lenB = strlen(B);
int p, q, newlenA = lenA;
  • lenA 和 lenB 分别存储两个字符串的长度

  • newlenA 初始化为A的长度,之后会随着删除字符而减小

  • p 和 q 是用于遍历的索引变量

 核心处理逻辑

for (int i = 0; i < lenB; i++) {
    p = q = 0;
    for (q = 0; q < newlenA; q++) {
        if (A[q] != B[i]) {
            A[p++] = A[q];
        }
    }
    newlenA = p;
}

这个双重循环是算法的核心:

  • 外层循环:遍历字符串B中的每个字符

    • 对于B中的每个字符B[i],我们需要从A中删除所有与之相同的字符

  • 内层循环:处理字符串A

    • p 和 q 都从0开始

    • q 是读取指针,遍历当前A字符串

    • p 是写入指针,指向下一个要写入的位置

    • 如果A[q]不等于当前B[i],则保留该字符(A[p] = A[q]并p++)

    • 如果相等,则跳过(不执行写入)

    • 处理完后,newlenA更新为当前有效长度(p的值)

 示例分析

以题目给出的样例为例:

A = "I love GPLT!  It's a fun game!"
B = "aeiou"

处理过程:

  • 第一次处理B[0]='a':

    • 删除A中所有'a'字符

    • A变为"I love GPLT! It's fun gme!"

  • 第二次处理B[1]='e':

    • 删除所有'e'字符

    • A变为"I lov GPLT! It's fun gm!"

  • 依此类推处理'i','o','u'...

  • 最终得到"I lv GPLT! It's fn gm!"

 字符串删除法(不推荐)

完整代码

#include <iostream>
#include <string>
using namespace std;

int main() {
    string str, del;
    getline(cin, str);
    getline(cin, del);

    for (char c : del) {
        size_t pos = str.find(c);
        while (pos != string::npos) {
            str.erase(pos, 1);
            pos = str.find(c);
        }
    }

    cout << str;
    return 0;
}

详细步骤

  • 输入读取

    • 使用 getline(cin, str) 读取第一行输入(字符串A)

    • 使用 getline(cin, del) 读取第二行输入(字符串B)

    • getline 可以读取整行,包括空格,直到遇到换行符

  • 字符删除处理

    • 使用范围for循环 for (char c : del) 遍历字符串B中的每一个字符

    • 对于B中的每个字符c:

      a. 在字符串A中查找该字符 (str.find(c))

      b. 如果找到 (pos != string::npos),则删除该字符 (str.erase(pos, 1))

      c. 继续查找该字符在字符串A中的下一个出现位置,直到找不到为止

  • 输出结果

    • 处理完成后,直接输出修改后的字符串A

 示例演示

以题目给出的样例为例:

A = "I love GPLT!  It's a fun game!"
B = "aeiou"

处理过程:

  • 删除所有'a':不影响(A中没有小写a)

  • 删除所有'e':"I lov GPLT! It's a fun gam!"

  • 删除所有'i':不影响

  • 删除所有'o':"I lv GPLT! It's a fun gam!"

  • 删除所有'u':"I lv GPLT! It's a fn gm!"

最终结果:"I lv GPLT! I's fn gm!"v

 Python解法

完整代码

a = input().strip('\n')
b = input().strip('\n')
bad_chars = set(b)
print(''.join([c for c in a if c not in bad_chars]))

输入处理

a = input().strip('\n')
b = input().strip('\n')
  • 这两行代码分别读取字符串A和字符串B。

  • strip('\n') 的作用是移除字符串末尾的换行符(如果有的话)。虽然题目说明输入以换行符结束,但 input() 函数通常会自动去掉末尾的换行符,所以这里的 strip('\n') 可能是为了确保万无一失。

 构建需要删除的字符集合

bad_chars = set(b)
  • 这里将字符串B转换为一个集合 bad_chars。集合的特点是:

    • 无序、不重复。

    • 查找操作(判断一个元素是否在集合中)的时间复杂度是 O(1),非常高效。

  • 因此,bad_chars 包含了所有需要从A中删除的字符(即B中出现的字符)。

 过滤字符

[c for c in a if c not in bad_chars]
  • 这是一个列表推导式,遍历字符串A中的每一个字符 c

    • 如果 c 不在 bad_chars 集合中(即 c 不是B中的字符),则保留该字符。

    • 否则,丢弃该字符。

  • 最终,这个列表推导式会生成一个列表,包含A中所有不被B包含的字符。

 拼接并输出结果

print(''.join([...]))
  • ''.join(...) 将过滤后的字符列表拼接成一个字符串。

  • print 直接输出这个字符串。

为什么这样设计?

  • 使用集合存储需要删除的字符

    • 集合的查找速度非常快(O(1)),比用列表或字符串直接查找(O(n))高效得多。

    • 对于长字符串(题目中长度可达 10^4),这种优化非常重要。

  • 列表推导式

    • 简洁高效地完成过滤操作。

    • 直接遍历A的每个字符,只保留不在B中的字符。

  • 直接输出

    • 无需额外存储结果,直接拼接后输出。

示例分析

以题目中的输入为例:

I love GPLT!  It's a fun game!
aeiou
  • 字符串A:I love GPLT! It's a fun game!

  • 字符串B:aeiou

  • bad_chars 集合:{'a', 'e', 'i', 'o', 'u'}(顺序可能不同,但无关紧要)

过滤过程:

  • 遍历A的每个字符,检查是否在 bad_chars 中:

    • I → 不在 → 保留

    • (空格)→ 不在 → 保留

    • l → 不在 → 保留

    • o → 在 → 删除

    • v → 不在 → 保留

    • e → 在 → 删除

    • ... 以此类推。

  • 最终保留的字符拼接为:I lv GPLT! It's fn gm!

 如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员小假

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值