数据结构与算法(C语言) | 字符串及KMP算法

本文深入探讨了字符串的存储结构,并对比分析了BF算法与KMP算法在字符串匹配过程中的效率和实现方式。BF算法虽然简单直观,但在最坏情况下时间复杂度较高;而KMP算法通过预处理模式串,利用匹配失败后的信息,显著减少了匹配次数,提高了搜索效率。

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

字符串的存储结构

字符串的存储结构与线性表相同,也分顺序存储结构和链式存储结构。

字符串的顺序存储结构是用一组地址连续的存储单元来存储串中的字符序列的

 

BF算法——Brute Force 属于朴素的模式匹配算法(效率低下):

–有两个字符串S和T,长度为N和M。首先S[1]和T[1]比较,若相等,则再比较S[2]和T[2],一直到T[M]为止;若S[1]和T[1]不等,则T向右移动一个字符的位置,再依次进行比较。

–该算法最坏情况下要进行M*(N-M+1)次比较,时间复杂度为O(M*N)。

int Index(String S,String T,int pos)
{
    int i = pos;    //i用于主串S中当前位置下标
    int j = 1;      //j用于子串中当前位置下标

    //0号下标存放字符串的长度
    while( i<= S[0] && j <= T[0])
    {
        if(S[i] == T[i])
        {
            i++;
            j++;
        }
        else        // 若失配则j回溯到第一个元素从新匹配
        {
            i = i-j+2;      // i回溯到上次匹配首位的下一个元素
            j = 1;
        }
    }

    if(j >T[0])
    {
        return i-T[0];
    }
    else
    {
        return 0;
    }

}

 

                                                  KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。

在KMP算法中,对于每一个模式串事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。

给模式匹配串添加一个k数组(也就是KMP中非著名的next()函数,函数本身包含了模式串的局部匹配信息)。

重点在于next()函数的理解:NEXT数组——当模式匹配串T失配的时候,NEXT数组对应的元素指导应该用T串的哪个元素进行下一轮的匹配。

可以尝试给出一串模式串验证得到的next值

如下

T

5

a

b

a

b

a

a

a

b

a

下标

0

1

2

3

4

5

6

7

8

9

next

 

0

1

1

2

3

4

2

2

3

第一位的next值为0,第二位的next值为1,后面求解每一位的next值时,根据前一位进行比较。 

void next(String T,int *next)
{
    
    //假设i是后缀下标
    //j是前缀下标
    int j = 0;
    int i = 1;
    next[1] = 0;
    while(i < T[0])
    {

        if(j==0 || T[i] == T[j])
        {
            i++;
            j++;
            next[i] = j;
        }
        else
        {
            //回溯
            j = next[j];
        }
        //前缀是固定的,后缀是相对的
    }

}

 利用已经部分匹配这个有效信息,保持i指针不回溯,通过修改j指针,让模式串尽量地移动到有效的位置

 

#include<stdio.h>

typedef char* String;

void get_next(String T,int *next)
{

    //假设i是后缀下标
    //j是前缀下标
    int j = 0;
    int i = 1;
    next[1] = 0;
    while(i < T[0])
    {

        if(j==0 || T[i] == T[j])
        {
            i++;
            j++;
            next[i] = j;
        }
        else
        {
            //回溯
            j = next[j];
        }
        //前缀是固定的,后缀是相对的
    }

}
//返回子串T在主串S第pos个字符之后的位置
//若不存在,则返回0
int Index_KMP(String S,String T,int pos)
{
    int i = pos;
    int j = 1;
    int next[255];
    get_next(T , next);

    while(i<=S[0] && j<= T[0])
    {
        if( j==0 || S[i] == T[j])
        {
            i++;
            j++;

        }
        else
        {
            j = next[j];
        }
    }

    if( j >T[0])//模式匹配最后一个元素之后
    {
        return i-T[0];
        //匹配成功,返回位置
    }
    else
    {
        return 0;
    }
}
int main()
{

    return 0;
}

 

### 关于C语言实现字符串匹配的数据结构算法 #### 字符串匹配简介 字符串匹配是指在一个较长的文本(主串)中寻找某个较短的子串(模式串)。常见的方法有暴力匹配法、KMP算法以及其他优化算法。暴力匹配法虽然时间复杂度较高,但在某些场景下仍然具有一定的实用价值。 #### 暴力匹配算法原理 暴力匹配算法通过逐一比较主串和模式串中的字符来判断是否存在匹配关系。如果当前字符不匹配,则回溯到下一个可能的起始位置继续尝试匹配[^1]。这种方法的时间复杂度为 \( O(m \times n) \),其中 \( m \) 和 \( n \) 分别为主串和模式串的长度。 以下是基于暴力匹配算法的具体实现: ```c int FindSubStr(char* t, char* p) { int i = 0, j = 0; while (p[i] != '\0' && t[j] != '\0') { if (p[i] == t[j]) { i++; j++; } else { j = j - i + 1; // 主串指针回退 i = 0; // 模式串重新从头开始匹配 } } if (p[i] == '\0') { // 如果模式串完全匹配成功 return j - i; } else { // 否则返回未找到的结果 return -1; } } ``` 上述代码实现了朴素的字符串匹配逻辑,能够满足基本需求[^2]。 #### 自动机识别字符串 除了暴力匹配外,还可以利用有限状态自动机(Finite State Automaton, FSA)来进行更高效的字符串匹配。在这种情况下,模式串被预处理成一个状态转移表,从而减少不必要的重复计算。这种方式特别适合多次查询同一模式的情况[^3]。 下面是构建简单自动机并执行匹配的核心思路: 1. 初始化状态矩阵 `next[sigma][state_count]` 表示每种输入符号下的下一跳转状态; 2. 预先填充该矩阵以便快速定位目标路径; 3. 使用实际输入流逐步推进直至发现完整匹配或者遍历结束。 尽管此方案较为抽象且初始化成本稍高,但对于大规模批量检索任务而言非常有效率。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值