2022-05-10 Boyer-Moore字符串查找算法,较KMP易于理解,甚至更快

有时候简单就是美

在被DFA KMP完虐后,我看到了BM算法,理念简单,效率一般极高,最优时间性能O(M/N),最差O(M*N),一般不会出现。

我们可以非常轻松的构建一个 right[ char ] 表,内容是 pat 匹配子字符串每个字母最后一次出现的位置,其余字母为 -1.

patABACother char
right[char]2123-1

看看如何用这个表:

还是如下对比,我们的对比方法是自右向左的对比。

txtAAARAABABABAC
txtPtr0123456789101112
patABAC
patPtr0123
right[A]2222
right[B]1111
right[C]3333
right[other]-1-1-1-1

第一个对比是 R 和 C ,R 在 right 表中是 -1,意思是模式字符串中就没有这个字符,既然没有,那么我在模式字符串长度范围都不可能匹配了,于是应该直接跳过整个 pat.size( ) 的长度,也就是 patPtr - right[ R ] = 3 - ( -1 ) = 4

txtAAARAABABABAC
txtPtr0123456789101112
patABAC
patPtr0123
right[A]2222
right[B]1111
right[C]3333
right[other]-1-1-1-1

如果比较的字符在 right 表中,同样的方法计算位移:patPtr - right[ A ] = 3 - 2 = 1

txtAAARAABABABAC
txtPtr0123456789101112
patABAC
patPtr0123
right[A]2222
right[B]1111
right[C]3333
right[other]-1-1-1-1

继续移动 2

txtAAARAABABABAC
txtPtr0123456789101112
patABAC
patPtr0123
right[A]2222
right[B]1111
right[C]3333
right[other]-1-1-1-1

继续移动 2

txtAAARAABABABAC
txtPtr0123456789101112
patABAC
patPtr0123
right[A]2222
right[B]1111
right[C]3333
right[other]-1-1-1-1

如果匹配,则 txtPtr 和 patPtr 同时回退,继续比较,直到遇到不匹配的字符或完全匹配

txtAAARAABABABAC
txtPtr0123456789101112
patABAC
patPtr0123
right[A]2222
right[B]1111
right[C]3333
right[other]-1-1-1-1

有个问题,如果移动距离 patPtr - right[ char ] < 0 ,这种情况是会出现的,此时我们设置移动距离为 1 即可。

通过以上示例,我们发现,对于普通的单词字符串,甚至是中文这种大字符表字符,我们都可以如此比较,而且效率相当彪悍。

以下是代码,很容易理解。

struct BoyerMoore
{
    explicit BoyerMoore(std::string patStr) : pat(std::move(patStr))
    {
        int patSize = static_cast<int>(pat.size());
        const int alphSize = 256;
        right = std::vector<int>(alphSize, -1);
        for (int patPtr = 0; patPtr != patSize; ++patPtr)
        {
            right[pat.at(patPtr)] = patPtr;
        }
    }
    auto search(const std::string &txt) const -> int
    {
        int txtSize = static_cast<int>(txt.size());
        int patSize = static_cast<int>(pat.size());
        int skip = 0;
        for (int txtPtr = 0; txtPtr <= txtSize - patSize; txtPtr += skip)
        {
            std::cout << txt.substr(txtPtr) << std::endl;
            std::cout << pat << std::endl;
            skip = 0;
            for (int patPtr = patSize - 1; patPtr >= 0; patPtr--)
            {
                if (pat.at(patPtr) != txt.at(txtPtr + patPtr))
                {
                    skip = patPtr - right[txt.at(txtPtr + patPtr)];
                    if (skip < 1)
                    {
                        skip = 1;
                    }
                    break;
                }
            }
            if (skip == 0)
            {
                return txtPtr;
            }
        }
        return txtSize;
    }

  private:
    std::vector<int> right;
    std::string pat;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不停感叹的老林_<C 语言编程核心突破>

不打赏的人, 看完也学不会.

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值