Day9|151.翻转字符串里的单词 KMP

151.翻转字符串里的单词

识别

这段代码实现了三个字符串处理功能:reverse 用于翻转字符串中指定范围的字符;removeExtraSpace 用于删除字符串两端和中间多余的空格;reverseWords 用于翻转字符串中的单词顺序。

核心/易错

核心功能是字符串的翻转和处理空格。易错点包括:

  • 确保字符串翻转时索引不越界。
  • 正确处理字符串中的空格,避免删除或翻转时出错。

难点/亮点

  • 难点:正确处理字符串中的空格,特别是在单词之间和字符串两端的空格。
  • 亮点reverseWords 函数通过先删除多余空格再翻转单词顺序的方式,实现了单词顺序的翻转,同时保持了单词内部字符的顺序。

算法设计思路

  1. reverse:通过两个指针从两端向中间遍历,交换字符直到两个指针相遇或交错。
  2. removeExtraSpace:首先找到字符串的非空格起始和结束位置,然后遍历字符串,跳过连续的空格,只保留单个空格。
  3. reverseWords:先调用 removeExtraSpace 函数去除多余空格,然后翻转整个字符串,最后遍历翻转后的字符串,翻转每个单词。

代码实现

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

// 翻转字符串中指定范围的字符
void reverse(char* s, int start, int end) {
    for (int i = start, j = end; i < j; i++, j--) { // 从两端向中间遍历
        int tmp = s[i]; // 交换字符
        s[i] = s[j];
        s[j] = tmp;
    }
}

// 删除字符串两端和中间多余的空格
void removeExtraSpace(char* s) {
    int start = 0; // 指向字符串开头的指针
    int end = strlen(s) - 1; // 指向字符串结尾的指针
    while (s[start] == ' ') start++; // 移动指针 start,直到找到第一个非空格字符
    while (s[end] == ' ') end--; // 移动指针 end,直到找到第一个非空格字符
    int slow = 0; // 指向新字符串的下一个写入位置的指针
    for (int i = start; i <= end; i++) { // 遍历整个字符串
        if (s[i] == ' ' && s[i+1] == ' ') { // 如果当前字符是空格,并且下一个字符也是空格,则跳过
            continue;
        }
        s[slow] = s[i]; // 否则,将当前字符复制到新字符串的 slow 位置
        slow++; // 将 slow 指针向后移动
    }
    s[slow] = '\0'; // 在新字符串的末尾添加一个空字符
}

// 翻转字符串中的单词
char * reverseWords(char * s){
    removeExtraSpace(s); // 先删除字符串两端和中间的多余空格
    reverse(s, 0, strlen(s) - 1); // 翻转整个字符串
    int slow = 0; // 指向每个单词的开头位置的指针
    for (int i = 0; i <= strlen(s); i++) { // 遍历整个字符串
        if (s[i] ==' ' || s[i] == '\0') { // 如果当前字符是空格或空字符,说明一个单词结束了
            reverse(s, slow, i-1); // 翻转单词
            slow = i + 1; // 将 slow 指针指向下一个单词的开头位置
        }
    }
    return s; // 返回处理后的字符串
}

// 主函数,用于测试上述函数
int main() {
    char s[] = "   hello world   ";
    printf("Original: '%s'\n", s);
    char *result = reverseWords(s);
    printf("Reversed: '%s'\n", result);
    return 0;
}

KMP

识别

实现了字符串匹配的两种算法:简单匹配(暴力匹配)和KMP算法。它定义了一个结构体 StrType 来存储字符串及其长度,并提供了相应的函数来分配字符串、执行简单匹配和KMP匹配。

核心/易错

核心功能是字符串匹配,易错点包括内存管理(如正确分配和释放内存)、边界条件处理(如字符串的起始和结束)以及KMP算法中next数组的正确计算。

难点/亮点

  • 难点:KMP算法的next数组计算是理解上的难点,需要正确处理子字符串的前缀和后缀匹配。
  • 亮点:代码结构清晰,将字符串处理和算法逻辑分开,易于理解和维护。

算法设计思路

  1. 简单匹配:通过两个索引分别遍历主字符串和子字符串,逐个字符比较,不匹配则子字符串从头开始重新匹配。
  2. KMP算法:通过预处理子字符串生成next数组,该数组用于在不匹配时调整子字符串的起始比较位置,避免从头开始。(next数组的核心遇见了冲突的位置要向前回退
    子串的最长相等前后缀的长度s[i]!= s[j]即前缀末尾和后缀末尾不匹配的时候
    j为他的前一位的next数组的值同时它也表示前缀的末尾
    B站代码随想录next数组代码17min48)

代码实现

#include <stdio.h>
#include <stdlib.h>

// 定义结构体,用于存储字符串及其长度
typedef struct {
    char *str;
    int length;
} StrType;

// 函数原型声明
int strAssign(StrType *str, const char *ch); // 字符串分配函数
int index_simple(const StrType *str, const StrType *subStr); // 简单匹配函数
int indexKMP(const StrType *str, const StrType *subStr, const int next[]); // KMP匹配函数
void getNext(const StrType *subStr, int next[]); // 计算next数组函数

// strAssign 函数实现,分配内存并复制字符串
int strAssign(StrType* str, const char* ch) {
    if (str->str) {
        free(str->str); // 释放原有内存
    }
    int len = 0;
    while (ch[len]) {
        ++len;
    }
    if (len == 0) {
        str->str = NULL;
        str->length = 0;
    } else {
        str->str = malloc(sizeof(char) * (len + 2)); // 分配内存,+2是为了存储'\0'和可能的扩展
        if (!str->str) {
            return 0; // 内存分配失败
        }
        str->str[0] = '\0'; // 0号索引填充'\0'
        for (int i = 1; i <= len; ++i) {
            str->str[i] = ch[i - 1];
        }
        str->length = len;
    }
    return len;
}

// 简单匹配(暴力匹配)函数实现
int index_simple(const StrType* str, const StrType* subStr) {
    int i = 1; // 主字符串索引
    int j = 1; // 子字符串索引
    while (i <= str->length && j <= subStr->length) {
        if (str->str[i] == subStr->str[j]) {
            ++i;
            ++j;
        } else {
            j = 1; // 重置子字符串索引
            i = (j > 0) ? i + 1 : 1; // 调整主字符串索引
        }
    }
    if (j > subStr->length) {
        return i - subStr->length; // 匹配成功,返回位置
    }
    return 0; // 匹配失败,返回0
}

// KMP算法的next数组计算函数实现
void getNext(const StrType* subStr, int next[]) {
    int i = 1;
    int j = 0;
    next[1] = 0; // 初始化
    while (i < subStr->length) {
        if (subStr->str[i] == subStr->str[j]) {
            ++i;
            ++j;
            next[i] = j;
        } else if (j > 0) {
            j = next[j];
        } else {
            ++i;
        }
    }
}

// KMP算法函数实现
int indexKMP(const StrType* str, const StrType* subStr, const int next[]) {
    int i = 1;
    int j = 1;
    while (i <= str->length && j <= subStr->length) {
        if (j == 0 || str->str[i] == subStr->str[j]) {
            ++i;
            ++j;
        } else {
            j = next[j]; // 根据next数组调整j
        }
    }
    if (j > subStr->length) {
        return i - subStr->length; // 匹配成功,返回位置
    }
    return 0; // 匹配失败,返回0
}

// main 函数实现
int main() {
    StrType str, subStr;
    int next[100]; // 假设next数组足够大

    // 为str和subStr分配字符串
    strAssign(&str, "这是一个示例字符串");
    strAssign(&subStr, "示例");

    // 计算next数组
    getNext(&subStr, next);

    // 使用简单匹配查找子字符串
    int simpleIndex = index_simple(&str, &subStr);
    printf("简单匹配找到子字符串的位置: %d\n", simpleIndex);

    // 使用KMP算法查找子字符串
    int kmpIndex = indexKMP(&str, &subStr, next);
    printf("KMP算法找到子字符串的位置: %d\n", kmpIndex);

    // 释放分配的内存
    free(str.str);
    free(subStr.str);

    return 0;
}

每行代码都添加了注释,以便更好地理解其功能和逻辑。

●卡码网:55.右旋转字符串
●28. 实现 strStr()
●459.重复的子字符串
●字符串总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值