数据结构-字符串(BF算法、KMP算法)


串的定义:

串(String)是计算机信息处理中最常见的一种数据结构,是有限的字符序列,记作
S = “a1a2a3…an
其中S是串名,双引号之间的字符序列为串的值,双引号是串的分界符,并不是串的成分。其中“ai(1<=i<n)”是程序设计语言的字符集中的字符,称之为串元素;n表示串的长度,当n=0时表示空串(Null String)。在计算机程序语言中串是一种特殊的线性表,其存储表示和线性表类似但又不完全相同。串的存储方式取决于将要对串所进行的操作。
例如: 假设有一个字符串
str = “Hello World!”
则str为串名引号中括起来的部分为串值,串值由10个字母字符、一个空格字符、一个感叹号字符组成,串长为12。

空串、空格串和子串

空串: 由零个字符组成的串,通常为两个相邻的双引号来表示空串,例如:s = “”,这两个双引号之间没有任何字符,故s表示一个空串,空串的长度为0。
空格串: 表示仅由空格字符(ASICII编码为32)组成的串通常在两个双引号之间包含一定的空格字符,例如:s = “ ”,这两个双引号之间存在有一些空格字符,故表示空格串。
子串: 串中任意个连续的字符组成的序列称为该串的子串。

注: 若串中存在空格,空格计算在串的长度中,例如:s = “I Love Programming”,则串s的长度为18。

在C语言中,使用双引号引起来的一个和单引号引起来的单个字符是存在区别的,例如:s1 = “A” 和s2 = 'A’两者是存在区别的,s1表示字符串而s2表示单个字符。

串在计算机中的三种存储方式:
  • 定长顺序存储方式: 将串定义成字符数组,利用 串名可以直接访问串值。用这种表示方式,串的存 储空间在编译时确定,其大小不能改变。
  • 堆分配存储方式: 仍然用一组地址连续的存储单 元来依次存储串中的字符序列,但串的存储空间是 在程序运行时根据串的实际长度动态分配的。
  • 块链存储方式: 是一种链式存储结构表示。

字符串匹配算法

字符串模式匹配(Pattern Matching)又称为子串定位运算。
在模式匹配中,一般将主串称为目标串,子串称为模式串。模式匹配成功是指在目标串中找到模式串;匹配不成功是指目标串中不存在模式串。模式匹配经常应用于各类文本编辑程序中对某一特定字符的查找,这样可以大大提高文本编辑程序的响应速度,响应性能。

一、BF算法(Brute Force)

即暴力(Brute Force)算法,是一种普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法,BF算法的时间复杂度为 O(n*m)(n是文本的长度,m是模式字符串的长度)。

bf算法

BF算法实现
/****************************************
 * 函数名称:BFIndex(ElemType *s, ElemType *t)
 * 功能描述:BF匹配算法主体
 * 传入参数:ElemType *s    传入的主串
 *          ElemType *t    传入的模式串
 * 返回值:若匹配完成返回模式串在主串的那个位
 *        置(主串中模式串首所在的下标位置)
 *        若未匹配到则返回-1
*****************************************/
int BFIndex(ElemType *s, ElemType *t)
{
    int i = 0;/*主串当前位置下标*/
    int j = 0;/*模式串当前位置下标*/
    int slen,tlen;
    slen = GetStringLen(s);
    tlen = GetStringLen(t);
    while (i < slen && j < tlen)
    {
        if(s[i] == t[j])
        {
            i++;/*相等,主串和模式串的下标整体后移一位*/
            j++;
        }
        else
        {
            i = i-j+1;/*不相等,主串后移,模式串返回串首*/
            j = 0;
        }
    }
    if(j == tlen)
    {
        return i - tlen;
    }
    else
    {
        return -1;
    }
}

BF算法在主串和子串匹配失败时,主串进行的回溯操作会影响整体匹配的效率,回溯之后,主串与子串有些部分比较是没有必要的。这种简单的丢弃前面的匹配信息是BF算法之所以效率低下的一个重要原因。

二、KMP算法(Knuth-Morris-Pratt)

Knuth-Morris-Pratt 算法简称为 “KMP 算法”,常用于在一个文本串 S 内查找一个模式串 P 的出现位置,这个算法由 Donald Knuth、Vaughan Pratt、James H. Morris 三人于 1977 年联合发表,故取这三人的姓氏命名此算法,KMP算法的时间复杂度为 O(n+m)(n是文本的长度,m是模式字符串的长度)。

其中在KMP算法中需要认真理解的就是其中的匹配过程到程序编写的转化,只有多次试验以及思考才能知道其中的含义!

下面是KMP算法的具体实现:

1. 获取字符串长度

/*************************************
 * 函数名称:GetStringLen(char *s)
 * 功能描述:获取字符串长度
 * 传入参数:待获取长度的字符串
 * 返回值:字符串长度
*************************************/
int GetStringLen(char *s)
{
    int len=0;
    while (s[len] != 0)
    {
        len++;
    }
    return len;
}

KMP算法的next[ ]数组求取方法:

next[ ]数组的作用: 当模式串发生失配时,指导文本串将以哪个字符与模式串进行比较。

Sdededddde
下标012345678
next[ ]-100123000
  • 假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,如果j = -1,或者当前字符匹配成功(即S[ i ] == P[ j ]),都令i++,j++,继续匹配下一个字符;
  • 如果j != -1,且当前字符匹配失败(即S[ i ] != P[ j ]),则令 i 不变,j = next[ j ]。这就意味着失配时,模式串P相对于文本串S向右移动了j - next [ j ] 位。
  • 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j - next[ j ],且此值大于等于1。

2. next[ ]数组获取

/*************************************
 * 函数名称:GetKMPnext(ElemType *p, int next[])
 * 功能描述:求KMP算法的next[]
 * 传入参数:ElemType *p    传入模式字符串
 *          int next[]     KMP算法的next[]
 * 返回值:空
*************************************/
void GetKMPnext(ElemType *p, int next[])
{
    int plen = GetStringLen(p);
    next[0] = 0;
    int i = -1;/*固定next[0] = -1*/
    int j;/*后缀*/
    while (j < plen)
    {
        /*通过寻找最长前缀后缀的方法确定next[]*/

        /*假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
        
        如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。这就意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。
        
        换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值,即移动的实际位数为:j - next[j],且此值大于等于1。*/
        if(-1 == i || p[i] == p[j])
        {
            ++i;
            ++j;
            next[j] = i;
        }
        else
        {
            i = next[i];
        }
    }
}

3. KMP算法实现

/****************************************
 * 函数名称:KMPIndex(ElemType *s, ElemType *t)
 * 功能描述:KMP匹配算法主体
 * 传入参数:ElemType *s    传入的主串
 *          ElemType *t    传入的模式串
 * 返回值:若匹配完成返回模式串在主串的那个位
 *        置(主串中模式串首所在的下标位置)
 *        若未匹配到则返回-1
*****************************************/
int KMPIndex(ElemType *s, ElemType *t)
{
    int i = 0;
    int j = 0;
    int next[255];

    int sLen = GetStringLen(s);/*获取字符串长度*/
    int tLen = GetStringLen(t);

    GetKMPnext(s,next);/*获取next[]数组*/
    while (i < sLen && j < tLen)
    {
        if(-1 == i || s[i] == s[j])
        {
            i++;
            j++;
        }
        else
        {
            j =next[j];/*找到下一次匹配的j值*/
        }
        
    }
    if(j == tLen)/*匹配完成*/
    {
        return i-j;/*返回相匹配位置第一个字符下标*/
    }
    else
    {
        return -1;
    }
}

😊
o_o …
(⊙o⊙)
🎈

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值