MST

星途 面试题库

面试题:C语言字符数组底层原理与高效字符串算法

在C语言中,基于字符数组实现一个高效的字符串匹配算法(如KMP算法)。要求详细解释算法的原理,包括如何构建部分匹配表。并且,分析该算法在不同规模数据下的时间复杂度和空间复杂度,同时说明如何利用字符数组的特性进行优化,以达到最佳性能。
29.2万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. KMP算法原理

KMP(Knuth - Morris - Pratt)算法是一种字符串匹配算法,用于在一个主串 text 中查找一个模式串 pattern。它的核心思想是利用已经匹配的部分信息,避免不必要的字符比较,从而提高匹配效率。

部分匹配表(Partial Match Table)

部分匹配表(也叫前缀函数),对于模式串 pattern 的每个位置 i,记录 patterni + 1 个字符组成的子串的最长相等前缀和后缀的长度(前缀和后缀不能是整个子串)。例如,对于模式串 "ABABACA",其部分匹配表为:[0, 0, 1, 2, 3, 0, 1]。

构建部分匹配表的过程如下:

  1. 初始化部分匹配表 next 数组,next[0] = 0
  2. 设两个指针 i = 1j = 0i 用来遍历模式串,j 表示当前已经匹配的前缀长度。
  3. i < patternLength 时:
    • 如果 pattern[i] == pattern[j],则 j++next[i] = ji++
    • 否则,如果 j > 0,则 j = next[j - 1],回到上一步继续比较。
    • 如果 j == 0,则 next[i] = 0i++

2. 代码实现

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

// 构建部分匹配表
void computeLPSArray(char *pattern, int patternLength, int *next) {
    int len = 0;
    next[0] = 0;
    int i = 1;
    while (i < patternLength) {
        if (pattern[i] == pattern[len]) {
            len++;
            next[i] = len;
            i++;
        } else {
            if (len != 0) {
                len = next[len - 1];
            } else {
                next[i] = len;
                i++;
            }
        }
    }
}

// KMP字符串匹配
int KMPSearch(char *text, char *pattern) {
    int textLength = strlen(text);
    int patternLength = strlen(pattern);
    int *next = (int *)malloc(patternLength * sizeof(int));
    computeLPSArray(pattern, patternLength, next);
    int i = 0; // 主串索引
    int j = 0; // 模式串索引
    while (i < textLength) {
        if (pattern[j] == text[i]) {
            i++;
            j++;
        }
        if (j == patternLength) {
            free(next);
            return i - j;
        } else if (i < textLength && pattern[j] != text[i]) {
            if (j != 0) {
                j = next[j - 1];
            } else {
                i++;
            }
        }
    }
    free(next);
    return -1;
}

3. 时间复杂度和空间复杂度分析

  • 时间复杂度
    • 构建部分匹配表的时间复杂度为 $O(m)$,其中 $m$ 是模式串的长度。
    • 匹配过程的时间复杂度为 $O(n)$,其中 $n$ 是主串的长度。
    • 因此,整个KMP算法的时间复杂度为 $O(n + m)$。
  • 空间复杂度
    • 需要额外的空间来存储部分匹配表,空间复杂度为 $O(m)$,其中 $m$ 是模式串的长度。

4. 利用字符数组特性优化

  • 减少不必要的内存访问:字符数组在内存中是连续存储的,利用这一特性,在匹配过程中可以减少缓存缺失。例如,在比较字符时,由于连续存储,相邻字符更有可能在同一缓存行中,从而提高访问速度。
  • 利用指针运算:可以使用指针来访问字符数组,指针运算在汇编层面效率更高。在KMP算法实现中,用指针代替数组下标访问,如 *(text + i) 代替 text[i],虽然现代编译器通常会对此进行优化,但在某些情况下仍能提高性能。