字符串算法学习笔记

一些简单概念的定义

  • 子串:字符串中连续的一段
  • 前缀:出现在字符串开始位置的子串
  • 后缀:出现在字符串结束位置的子串
  • 真前缀:除字符串本身外的所有前缀
  • 真后缀:除字符串本身外的所有后缀

给定一个字符串T(文本) 和一个单词P, 问P在T中出现过几次?分别在哪几个位置出现?

一些约定:

  • n和m: 文本T的长度、P的长度
  • ?_?: 字符串P的第i个字符(下标从1开始)
  • ?[?…?]: 字符串P的第l个字符到第r个字符组成的字符串

朴素暴力解法

枚举T中的每个位置,检查P是否在此出现

最差时间复杂度为?(??)

kmp算法

KMP算法的主要思想是利用以比较结果跳过一些无效比较,是一种解决字符串匹配问题的线性时间算法
使用了一个辅助数组F,F定义如下:
- F[i]是一个的比i小的,最大的,满足 P[1…F[i]] 是P[1…i]的一个真后缀的数;
即每个F[i]都是P[1…i]的border(一个字符串中与真后缀相同的真前缀的最大长度)

在这里插入图片描述
F[i]代表最大的比i小的数,并且满足 P[1…i] 是P的一个真后缀

利用F数组快速匹配
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码实现

void getMatch(char *P,int m,char *T,int n)
{
    getF(P,m);
    int k=0;
    for(int i = 1; i <= n; ++i) 
    {
        while(k&&T[i]!=P[k+1]) k=F[k];
        if(T[i]==P[k+1]) ++k;
        if(k==m)
         {
            printf("matched at %d\n", i-m+1);
            k=F[k];
        }
    }
}

如何求出F数组
朴素求法:
枚举P[1…i]的前缀,check是否是P[1…i]的后缀,最长的满足条件的前缀即为F[i]
最差时间复杂度为?(?^? )

观察一下:
如果P[1…F[i]]是P[1…i]的一个后缀,那么P[1…F[i]-1]一定是P[1…i-1]的一个后缀;
再观察一下:
令Q=P[1…i]
P[1…F[i]]是Q的后缀
P[1…F[F[i]]]是Q的后缀
P[1…F[F[F[i]]]]是Q的后缀
P[1…F[F[F[F[i]]]]]是Q的后缀
P[1…F[F[F[F[F[i]]]]]]是Q的后缀

不断的把i带入F[i]得到的字符串P[1…F[i]]都是P的一个后缀

在这里插入图片描述
代码实现

void getF(char *P,int m) {
    F[1]=0;
    for(int i = 2; i <= m; ++i)
     {
        int k=F[i-1];
        while(k&&P[k+1]!=P[i]) k=F[k];
        if(P[k+1]==P[i]) ++k;
        F[i]=k;
    }
}

挖坑

Z-algorithm (求字符串每个后缀与原串的LCP)

AC自动机 (多串前缀匹配的类似于KMP的算法)

Manacher算法

后缀数组

后缀自动机

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值