03笔记 数据结构——串(字符串)—— 基于《数据结构》(C语言版) 第2版 (严蔚敏等)和《数据结构教程》蔡子经,施伯乐

基本概念和储存结构

字符集V上字符组成任何有限序列
如C语言中的“abcd"
可以看作是一个特殊的线性表:(只含有字符型变量)一般用顺序储存结构来储存表

运算

#include<stdio.h>

#define MAXN 1000
char s[MAXN], s1[MAXN], s2[MAXN];
const int fail = 0, false = 0;
const int suc = 1, true = 1;

int strlen(char s[])
{
    int i;
    for(i = 0; s[i] != 0; i++){}
    return i;
}

int strcat(char s1[], char s2[])//s1 += s2
{
    int l1 = strlen(s1);
    int l2 = strlen(s2);
    if(l1 + l2 >= MAXN)
        return fail;
    for(int i = 0; i <= l2; i++)// '\0'要考虑进去
        s1[l2 + i] = s2[i];
    return suc;
}

int strsub(char s1[], int pos, int len, char s2[])
{
    //s1的pos位置取len长度的字符变成s2
    int l1 = strlen(s1);
    int l2 = strlen(s2);
    if(pos < 0 || pos > l1)
        return fail;
    if(len < 0 || pos + len > l2)
        return fail;
    
    for(int i = 0; i < len; i++)
        s2[i] = s1[pos + i];
    s2[len] = 0;
    return suc;
}

int strins(char s1[], int pos, char s2[])
{
    //s2插在s1的pos位置上
    int l1 = strlen(s1);
    int l2 = strlen(s2);
    if(pos < 0 || pos > l1 || l1 + l2 > MAXN)
        return fail;
    for(int i = l1; i >= pos; i--)
        s1[l2 + i] = s1[i];
    for(int i = 0; i < l2; i++)
        s1[pos + i] = s2[i];
    return suc;
}

int strdel(char s1[], int pos, int len)
{//pos删去len个字符
    int l1 = strlen(s1);
    if(pos < 0 || pos > l1)
        return fail;
    if(pos + len >= l1)
        s1[pos] = 0;
    else
        for(int i = 0; i <= l1 - pos - len; i++)
            s1[pos + i] = s1[pos + i + len];
    return suc;
}

匹配

BF算法

拿小的串和大的串对齐,每次遇见不匹配的情况向后移动一格,再一一匹配
算法复杂度为O(nm)可以看成O(n²)

KMP算法

在这里插入图片描述
这里不匹配的是a和f,那么我们应该找8字符串的f前面的c来判断要移动多少步,如果直接移动6步,就会缺失掉移动三步时候刚好匹配的情况

如何找短的字符串每个位置对应移动的步数?
这时候我们需要观察字符串的情况了
当f出现匹配失误的时候,我们就得看前面abcabc的部分来看“后缀”和“前缀”是否一样
在这里插入图片描述
不难发现当长度为3的时候,前缀和后缀是一样的,这个时候,我们要往后移动6-3=3步,保证你下一次匹配的时候,abc这部分仍然可以匹配上前面部分的abc,此时从第四个位置a开始一一比对
在这里插入图片描述
(长度为6的子集必定相等,这个时候是移动6-6=0步,没有意义)

所以我们很容易得出子串每个位置对应的next比对内容
(注意:若为i匹配不上,下一个匹配的是next[i-1],也就是i-1的数字对应的前后缀)

“” 当第一个字符不匹配,我们默认为-1(也可以是0,只是差一个数字罢了)
a,公共前后缀是他自身,往后移动 1 - 0 = 1步,光标移动到a上 为 0
ab, 公共前后缀只有2(本身),所以我们可以认为没有公共前后缀,移动的大小为 2 - 0 = 2步,在程序上就是把比对的光标移到a上 为 0
abc 同上,移动 3 - 0 = 3,把光标移动到a上 0
abca 前后缀最大为1(把等于本身的去掉),所以移动 4 - 1 = 3步,把光标移动到b 1
abcab 前后缀最大为2,所以移动 5 - 2 = 3步,把光标移动到c 2
abcabc 前后缀最大为 3,移动 6 - 3 = 3 步,光标移动到c
于是我们得到如下表格
在这里插入图片描述
得到next数组代码

KMP优化

(待补充

BM算法

检验26个字母(可以把标点符号和数字也加上)在短串的位置 (自右向左)
有如下情况
①字母不在短串中 || 字母是短串的最后一个位置 : 往右移动s.length()
②字母在短串中且不是最后一个位置 往右移动 s.length() - i - 1 (这的i是字母在子串中出现的最大位置)
比如return这个子串,r出现在0位和4位,取4
于是 f(n) = 6, f( r ) = 4, f(u) = 3 f(t) = 2 f(e) = 1,其他所有字母 = 6

当你不匹配的时候,匹配的子串每次移动 的是, 子串上一次最后一个字符 对应位置的 主串位置的alpha

举个例子
retuenereturn
return
这里n对应的上,但是r对应不上,这里往右移动的应该是f(n) = 6
reeurnnnreturn
return
这里e对不上,往右移动也是看最后一个字符n,f(n) = 6

BM和KMP算法都减少了许多不必要的匹配,可以将时间复杂度缩小到O(n)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值