C语言经典例96-计算字符串中子串出现的次数

本文介绍了一种计算字符串中子串出现次数的方法,包括使用暴力法进行串的模式匹配,以及利用C语言实现的具体代码示例。通过双重循环对比母串与子串的每个字符,若完全匹配则计数器加一,最终返回计数结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 题目

计算字符串中子串出现的次数

2 分析

串的模式匹配在数据结构中有KMP算法,但这个算法实现起来繁琐,难度很大,所以对于一般来说,使用暴力法求解,设有字符串str和子串sub,第一轮循环从str的第一个字符开始,设置一个位置标号j,再与子串sub逐个比较,若比较相同的话,则位置标号向后移,比较下一个字符,出现第一个不相同字符则该轮比较结束,比较结束后若j的值和sub字符串长度相同,则说明找到一个子串,计数count递增,第二轮循环则从str的第二个字符开始比较, 依次类推。

另外在string.h有函数strstr(),该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。函数原型如下:

char *strstr(const char *haystack, const char *needle)
  • haystack – 要被检索的 C 字符串。
  • needle – 在 haystack 字符串内要搜索的小字符串。

3 实现

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

int subString(char *str,char *sub)
{
    int count = 0, i, j;
    for (i = 0; i < strlen(str); i++) {
        for (j = 0; j < strlen(sub); j++) {
            if(str[i + j] != sub[j]) {
                break; // 出现了不同字符就退出循环
            }
        }
        if (j == strlen(sub)) {
            count++; // 退出循环后若j的值等于子串的长度,则存在子串
        }
    }
    return count;
}

int main(void)
{
    char str[100],sub[50];
    printf("请输入母串:");
    gets(str);
    printf("请输入子串:");
    gets(sub);
    printf("%d", subString(str,sub));
    return 0;
}

4 运行结果

请输入母串:abcaabcaadbbc
请输入子串:bc
3
<think>好的,我现在需要帮助用户解决如何用C语言统计字符串中子出现次数的问题。首先,我得回忆一下用户提供的引用内容,看看有没有相关的示代码或者思路。用户提供了五个引用,都是关于这个问题的不同实现方法,我需要综合这些信息给出一个清晰的解决方案。 首先,我应该确定这个问题的基本思路。统计子出现的次数,通常需要遍历父,逐个检查是否存在与子匹配的部分。如,在引用2和引用4中,都是通过双重循环实现的:外层循环遍历父的每个字符,内层循环检查当前位置开始的字符是否与子完全匹配。如果匹配成功,则计数器增加。 接下来,我需要考虑如何优化代码,避免重复检查或者越界访问。如,在父的某个位置,如果剩下的字符长度已经小于子的长度,那么就不需要继续检查了,这样可以减少不必要的循环次数。引用4中的代码使用了strlen(a)作为外层循环的条件,但可能在每次循环中都会计算一次strlen(a),这会影响效率,应该提前计算好长度。 另外,用户提供的引用5中有一个案,统计子"aa"出现的次数,但它的实现方式比较特殊,只针对特定子,而且每次循环i加2,这可能不适用于一般情况。所以,我需要确保我的解决方案能够处理任意长度的子,而不仅仅是固定长度的。 接下来,我应该考虑函数的参数和返回值。引用3提到使用一个自定义函数int countStrNum(char* str, char* subStr),这样可以将功能模块化,便于重用。因此,我会建议用户编写一个这样的函数,然后在主函数中调用。 然后,我需要处理边界情况,比如子长度为0或者父长度小于子的情况,这时候应该直接返回0。此外,当子长度大于父时,显然不可能存在,所以这也是需要考虑的情况。 在代码实现方面,外层循环应该从父的0到strlen(parent) - sub_len的位置,因为如果剩余的字符不足子长度,就不可能匹配。内层循环则逐个字符比较,一旦发现不匹配就跳出,否则继续。如果内层循环完全匹配,则计数器加1。 另外,需要注意字符串的结束符'\0',在比较时,确保不会越界访问。如,在引用2的代码中,外层循环是i < x,而内层循环是j < y,这里x是父长度,y是子长度。当i + j超过父长度时,可能会访问到无效内存,所以应该在外层循环的条件中限制i的最大值为x - y,这样可以避免越界。 现在,我需要将这些思路整理成一个步骤清晰的解决方案。首先,计算和子的长度;然后,遍历父,每次检查从当前位置开始的子是否匹配;如果匹配,计数器加1,并继续后续的检查。同时,要注意到子可能重叠的情况,如父"aaaa"中子"aa"出现3次,所以每次外层循环i应该递增1,而不是子的长度,除非用户要求不重叠的情况。 不过,根据用户提供的引用中的示代码,似乎都是允许重叠的,比如引用4中的代码每次i递增1。因此,在解决方案中应该保持这种方式,除非用户特别说明不需要重叠。 最后,我需要测试一下这个算法,确保它在不同情况下都能正确工作,比如子出现在开头、中间、结尾,或者多次重叠出现的情况。 总结一下,步骤应该是: 1. 获取父和子,并计算它们的长度。 2. 处理特殊情况,如子长度为0或父长度不足。 3. 遍历父,每次检查从当前位置开始的子是否匹配。 4. 匹配成功则增加计数器。 5. 返回计数器的值。 现在,将这些步骤转化为C语言代码,并确保语法正确,比如使用strlen函数需要包含string.h头文件,字符串输入使用gets或者更安全的fgets函数(但用户提供的引用中使用gets,需要注意可能存在缓冲区溢出的问题,但用户可能只是示,所以暂时按引用中的方式处理)。 最后,给出示代码,并解释关键部分,比如循环的条件,比较过程等。同时,提醒用户可能的注意事项,比如子长度、重叠情况等。</think>统计字符串中子出现次数的C语言实现方法主要分为以下步骤: ### 实现步骤 1. **输入处理**:获取父和子输入 2. **边界判断**:处理空字符串或子长度大于父的情况 3. **双重循环检测**- 外层循环遍历父每个字符 - 内层循环验证子是否匹配 4. **计数规则**:允许子重叠出现(如"aaaa"中"aa"出现3次) ### 实现代码 ```c #include <stdio.h> #include <string.h> int countSubstr(const char* str, const char* substr) { int count = 0; int str_len = strlen(str); int sub_len = strlen(substr); if (sub_len == 0 || str_len < sub_len) return 0; for (int i = 0; i <= str_len - sub_len; i++) { int match = 1; for (int j = 0; j < sub_len; j++) { if (str[i + j] != substr[j]) { match = 0; break; } } if (match) count++; } return count; } int main() { char parent[100], child[100]; printf("请输入父字符串:"); gets(parent); printf("请输入子字符串:"); gets(child); int result = countSubstr(parent, child); printf("出现次数:%d\n", result); return 0; } ``` ### 代码解析 1. **双重循环结构**:外层循环控制父遍历起点,内层循环验证子完整匹配 2. **边界控制**:`i <= str_len - sub_len` 避免数组越界访问 3. **匹配标记**:使用match变量记录当前起始位置是否完全匹配 4. **效率优化**:提前计算字符串长度,避免在循环中重复调用strlen[^2][^4] ### 注意事项 1. 使用gets函数存在缓冲区溢出风险,建议改用`fgets(str, sizeof(str), stdin)` 2. 该算法时间复杂度为$O(n*m)$(n=父长度,m=子长度) 3. 若需要非重叠统计,匹配成功后应将i增加`sub_len-1`[^5]
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值