B2119 删除单词后缀
#include <iostream>
using namespace std;
# include <string.h>
#include <ctype.h>
#include <algorithm>
#include <string.h>
int main(){
string word;
cin>>word;
if(word.size()> 2 && word.substr(word.size() - 2) == "er"){
word = word.substr(0,word.size() - 2);
}
else if(word.size() > 2 && word.substr(word.size() - 2 ) == "ly"){
word = word.substr(0,word.size() - 2);
}
else if(word.size() > 3 && word.substr(word.size() - 3 ) == "ing") {
word= word.substr(0,word.size() - 3);
}
cout<<word;
}
/*### `strcmp(word + len - 2, "er")` 的解释
`strcmp` 是一个 C 标准库函数,用于**比较两个 C 风格的字符串**(即 `char` 数组)。它逐个字符比较两个字符串的 ASCII 值,直到遇到不同的字符或遇到字符串的终止符 `\0`。它的典型使用形式是 `strcmp(str1, str2)`,其返回值如下:
- 如果 `str1` 等于 `str2`,返回 `0`。
- 如果 `str1` 小于 `str2`(按字典顺序),返回负值。
- 如果 `str1` 大于 `str2`(按字典顺序),返回正值。
### `strcmp(word + len - 2, "er")` 的具体含义
- `word + len - 2` 是一个指针运算,表示从字符串 `word` 的倒数第二个字符开始。换句话说,它将 `word` 的内存地址偏移到 `len - 2` 的位置。
- 例如,如果 `word` 是 `runner`,且 `len` 为 `6`,`word + len - 2` 就指向 `e`,因此相当于指向字符串 `"er"` 的起始位置。
- `strcmp(word + len - 2, "er")` 的作用是:
- 比较从 `word` 的倒数第二个字符开始的子字符串与 `"er"` 是否相等。
- 如果 `word` 以 `"er"` 结尾,`strcmp` 将返回 `0`,表示两者相等。
- 如果 `word` 不是以 `"er"` 结尾,`strcmp` 会返回非零值。
### 使用这种方法的原因
- 由于 `strcmp` 比较的是两个完整的字符串,所以我们通过将指针移动到 `word` 的末尾以只比较后缀部分。
- 这样可以高效地判断一个字符串是否以某个特定的后缀结尾,而无需手动创建新的字符串或使用复杂的循环。
### 示例说明
假设有以下代码:
```c
char word[] = "runner";
int len = strlen(word);
if (strcmp(word + len - 2, "er") == 0) {
printf("The word ends with 'er'.\n");
} else {
printf("The word does not end with 'er'.\n");
}
```
- `len` 是 `6`,`word + len - 2` 指向字符串 `"er"` 的起始位置。
- `strcmp(word + len - 2, "er")` 比较的是字符串 `"er"` 与 `"er"`,结果返回 `0`,说明 `word` 确实以 `"er"` 结尾。
### 总结
- `strcmp` 的这种用法利用了指针偏移,直接从字符串的末尾位置开始比较。
- 这是在 C 语言中判断字符串后缀的一种有效方法,因为它避免了额外的字符串分配和拷贝。 */
你可以使用一个 `for` 循环来遍历 `patterbs` 数组,从而判断一个单词是否以指定的后缀(如 `er`、`ly`、`ing`)结尾。这个方法更加灵活和简洁,避免了多次 `if-else` 判断。以下是具体的实现方法:
### 示例代码
```cpp
#include <iostream>
#include <cstring>
using namespace std;
int main() {
char word[33]; // 假设输入单词最大长度为 32
cin >> word;
// 定义后缀数组
char patterns[][4] = {"er", "ly", "ing"};
int len = strlen(word);
// 遍历后缀数组进行匹配
for (int i = 0; i < 3; ++i) {
int patternLen = strlen(patterns[i]);
if (len > patternLen && strcmp(word + len - patternLen, patterns[i]) == 0) {
// 删除后缀
word[len - patternLen] = '\0';
break; // 退出循环,避免多次删除
}
}
cout << word << endl;
return 0;
}
```
### 解释
1. **定义后缀数组**:`char patterns[][4] = {"er", "ly", "ing"};`
- `patterns` 是一个二维字符数组,存储需要检查的后缀。每个字符串的长度最多为 `3`,包括空字符 `\0`。
2. **遍历后缀数组**:
- 使用 `for (int i = 0; i < 3; ++i)` 遍历所有后缀。
- `strlen(patterns[i])` 获取当前后缀的长度 `patternLen`。
3. **检查并删除后缀**:
- `if (len > patternLen && strcmp(word + len - patternLen, patterns[i]) == 0)` 检查 `word` 是否以当前后缀结尾。
- `len > patternLen` 确保单词长度大于当前后缀长度,避免越界访问。
- `strcmp(word + len - patternLen, patterns[i]) == 0` 用于比较单词的最后 `patternLen` 个字符和 `patterns[i]`。
- 如果匹配成功,将单词的末尾截断,即 `word[len - patternLen] = '\0';`,从而删除后缀。
- `break` 语句用于防止多次匹配和删除后缀。
### 代码优势
- **简洁性**:通过一个 `for` 循环和 `patterns` 数组,避免了多次 `if-else` 语句,代码更易于扩展和维护。
- **灵活性**:可以轻松扩展新的后缀,只需要在 `patterns` 数组中添加新元素。
- **效率**:使用 `strcmp` 和指针偏移操作实现快速判断和截断。
在你的示例中,`char patterns[][4] = {"er", "ly", "ing"};` 定义了一个 **二维字符数组**,用于存储需要检查的后缀字符串。让我详细解释这个定义的原理和细节。
### 二维字符数组的定义
1. **`char patterns[][4]` 的含义**:
- 这是一个二维字符数组,其中每一行(即每个元素)都是一个**字符串**。
- `4` 表示每个字符串最多可以包含 `3` 个有效字符加上一个**空字符 `\0`**(用于表示字符串结束)。因此,每个字符串的总长度是 `4` 字节。
2. **为什么使用 `4`**:
- 这是因为最长的字符串是 `"ing"`,包含 `3` 个字符,加上末尾的空字符 `\0`,需要 `4` 个字节的空间。
- 如果定义为 `char patterns[][3]`,就无法存储 `"ing"`,因为它需要额外的一个字节来存储 `\0` 终止符。
### 如何存储字符串
- `patterns` 是一个二维数组,每个元素实际上是一个 `char` 数组,也可以被视为一个字符串。
- 在内存中,`patterns` 的布局如下:
```
patterns[0] = "er\0"
patterns[1] = "ly\0"
patterns[2] = "ing\0"
```
每个字符串末尾都有一个 `\0` 作为字符串结束标志。
### 存储和访问方式
- **存储**:
- `char patterns[][4]` 创建了一个包含 `3` 行、`4` 列的二维数组,总共占用 `3 x 4 = 12` 个字节的内存。
- 每行是一个独立的 `char` 数组(字符串),所有行共享一个连续的内存空间。
- 例如,`patterns[0]` 是一个指向 `"er"` 的 `char` 数组,`patterns[1]` 指向 `"ly"`,`patterns[2]` 指向 `"ing"`。
- **访问**:
- `patterns[i]` 是一个指针,指向 `patterns` 中的第 `i` 行,即 `patterns[i]` 本身是一个 `char*`,表示一个字符串。
- 可以通过 `strcmp(patterns[i], "er")` 或 `strlen(patterns[i])` 等字符串函数来操作这些字符串,就像处理普通的 `char*` 一样。
### 二维字符数组与一维字符串数组的区别
- **一维字符数组**:用于存储单个字符串。例如,`char word[33]` 可以存储一个最多为 `32` 个字符的字符串(包含终止符)。
- **二维字符数组**:用于存储多个字符串,每个字符串都有自己的长度和终止符。例如,`char patterns[][4]` 可以存储多个最长为 `3` 个字符的字符串,每个字符串末尾有 `\0`。
### 示例总结
```cpp
char patterns[][4] = {"er", "ly", "ing"};
```
- 这是一个二维字符数组,存储了三个字符串 `"er"`、`"ly"` 和 `"ing"`,每个字符串最多有 `3` 个字符加 `1` 个空字符。
- 你可以通过 `patterns[i]` 访问每个字符串,并使用标准的字符串操作函数(如 `strcmp`、`strlen`)对其进行操作。
是的,你理解得很对。`char patterns[][4] = {"er", "ly", "ing"};` 这种写法是允许的,因为编译器可以**自动推断出数组的行数**。这是 C 和 C++ 中二维数组定义的一种灵活性表现。
### 为什么可以省略行数 `3`
- **推断数组的大小**:当你定义 `char patterns[][4]`,并在初始化时提供了 `{"er", "ly", "ing"}` 三个元素,编译器会自动根据初始化列表的元素数量推断出行数为 `3`。
- **强制指定列大小**:列数 `4` 必须显式指定,因为在内存布局中,每行的大小是固定的,编译器需要知道每行能够存储多少个字符。
- 每个字符串最多为 `3` 个字符,加上字符串结束符 `\0`,总共需要 `4` 个字符的空间。因此,我们必须指定 `4`。
- 如果省略列数 `4`(即写成 `char patterns[][] = {"er", "ly", "ing"}`),编译器会报错,因为它无法推断出每行的大小。
### 如何解释 `char patterns[3][4]`
如果我们显式地写成 `char patterns[3][4] = {"er", "ly", "ing"};`,这表示一个二维字符数组,包含 `3` 行、`4` 列。每行都是一个 `char[4]` 数组,可以存储一个字符串,最多 `3` 个字符加 `1` 个空字符 `\0`。
### 编译器如何推断
- 在 `char patterns[][4] = {"er", "ly", "ing"};` 中,编译器根据提供的初始化列表 `{ "er", "ly", "ing" }` 看到有 `3` 个字符串,所以推断出数组有 `3` 行。
- 这就等效于写成 `char patterns[3][4] = {"er", "ly", "ing"};`。
### 省略行数的好处
- **简洁性**:在初始化时,如果你知道要存储的字符串数量,省略行数可以让代码更简洁。
- **灵活性**:如果以后修改了初始化列表的大小,编译器会自动调整行数,而不需要手动更改数组声明中的行数。
### 例子总结
```cpp
char patterns[][4] = {"er", "ly", "ing"};
```
- 编译器推断出有 `3` 行。
- 每行有 `4` 列,存储 `3` 个字符和一个终止符 `\0`。
- 等效于 `char patterns[3][4] = {"er", "ly", "ing"};`,但使用省略行数的写法更为简洁。
在这个例子中:
- 当 `word.size()` 是 `6` 时,`word.size() - 2` 计算出的是 `4`。
- `word.substr(word.size() - 2)` 从索引 `4` 开始,提取 `2` 个字符。这确实会包含从索引 `4` 开始的字符和索引 `5` 的字符(即最后两个字符)。
### 说明
- **索引从 `0` 开始**:字符串的索引是从 `0` 开始的,因此 `word.size() - 2` 指的是字符串倒数第二个字符的索引。
- **`substr()` 函数的行为**:`word.substr(start)` 生成从 `start` 索引开始的子字符串,直到字符串的末尾。`word.substr(start, length)` 则生成从 `start` 开始、长度为 `length` 的子字符串。
### 举例
假设 `word = "runner"`:
- `word.size()` 是 `6`。
- `word.size() - 2` 是 `4`,这对应于字符 `'e'` 的索引位置。
- `word.substr(4)` 或 `word.substr(4, 2)` 会提取从索引 `4` 开始的 `2` 个字符,即字符串 `"er"`。
### 总结
- **包含了第 `4` 个字符和第 `5` 个字符**:`word.substr(4, 2)` 包括索引 `4` 和 `5` 的字符,即字符串 `"er"`。
- 因为索引是从 `0` 开始,所以 `word.size() - 2` 确实表示倒数第二个字符的起始位置。
在这个示例中,我们使用了 `strcmp(word + len - patternLen, patterns[i])` 语句,它的作用是比较两个字符串:`word + len - patternLen` 和 `patterns[i]`。这是一个比较字符串尾部子字符串和给定模式的标准方式。让我详细解释其中的原理和工作机制:
### 1. **`strcmp` 的基本原理**
- `strcmp` 是一个来自 C 标准库 `<cstring>` 的函数,用于比较两个 C 风格的字符串。
- 它逐字符比较两个字符串的 ASCII 值,直到遇到不同的字符或遇到字符串结束符 `\0`。
- 返回值:
- 如果两个字符串相等,`strcmp` 返回 `0`。
- 如果第一个字符串小于第二个字符串(按字典顺序),返回负值。
- 如果第一个字符串大于第二个字符串,返回正值。
### 2. **`strcmp(word + len - patternLen, patterns[i])` 的含义**
- `word + len - patternLen` 是一个指针运算,表示指向字符串 `word` 的某个位置。
- `word` 是一个字符数组或指针,`len` 是 `word` 的总长度,`patternLen` 是我们要匹配的模式的长度(例如 `2` 或 `3`)。
- `word + len - patternLen` 让指针从字符串末尾向前移动 `patternLen` 个字符,指向可能的后缀开始位置。
- 例如,如果 `word = "running"`,`len = 7`,`patternLen = 3`,那么 `word + len - patternLen` 指向字符串 `"ing"` 的起始位置。
- `patterns[i]` 是我们要比较的模式字符串,如 `"er"`、`"ly"`、`"ing"` 等。
- `strcmp(word + len - patternLen, patterns[i])` 通过 `strcmp` 函数从指针位置开始比较 `word` 字符串的尾部和 `patterns[i]`。
### 3. **工作机制**
- 当 `strcmp(word + len - patternLen, patterns[i])` 被调用时,`strcmp` 会从 `word + len - patternLen` 指针开始,与 `patterns[i]` 逐字符进行比较。
- 如果两者完全相等(包括长度和内容),`strcmp` 返回 `0`,表示匹配成功。
- 如果不同,则返回一个非零值。
### 4. **示例操作**
假设 `word = "running"`,我们想判断它是否以 `"ing"` 结尾:
1. 计算长度:
- `len = 7`(长度为 `7`,`r-u-n-n-i-n-g`)。
- `patternLen = 3`(`"ing"` 的长度)。
2. 计算比较位置:
- `word + len - patternLen = word + 7 - 3 = word + 4`。
- 这意味着从 `word[4]` 开始,子字符串是 `"ing"`。
3. 比较:
- `strcmp(word + 4, "ing")` 会比较 `word[4]` 到 `word[6]` 的内容与 `"ing"` 是否匹配。
- 如果匹配,返回 `0`,否则返回非零值。
### 5. **为什么使用这种方式?**
- 这种方式利用了指针运算的强大功能,可以高效地从字符串的任意位置开始比较,而不需要额外的字符串复制或创建新字符串。
- 通过计算字符串长度和模式长度,我们可以灵活地检查字符串是否以特定后缀结尾,而不需要手动构建和遍历每个字符。
### 总结
- `strcmp(word + len - patternLen, patterns[i])` 的作用是从 `word` 字符串的尾部开始,与 `patterns[i]` 进行逐字符比较。
- 这种方式依赖于指针运算和 `strcmp` 的逐字符比较功能,能够高效判断字符串的尾部是否匹配特定模式。