本题要求你计算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!
目录
浅说:这道题有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;
:定义两个字符串变量a
和b
,分别用于存储输入的字符串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
,说明c
在b
中出现过,跳过不输出。
示例分析
输入:
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'] = true
,v['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!
如果小假的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!