《数据结构:从0到1》-12-高级字符串算法

高级字符串算法解析

引言

大家都在海边沙滩上找过贝壳,如果沙滩很小可以一寸一寸地找。但如果面对的是整个海滩,这种笨办法就行不通了。字符串搜索也是一样,在几十KB的文本里,暴力搜索还能应付。但在大数据量的日志文件或整个互联网中搜索,就需要更高级的算法。

今天我们带着这些疑问给大家详细介绍下这些高级字符串算法,了解它们背后的原理。


一、 Rabin-Karp算法

1.1 核心思想

举个例子:警察找人时,不需要把每个人都带回去详细检查,只需要对比指纹。如果指纹匹配,再进一步确认身份。

Rabin-Karp就是这个思路:

  • 指纹 = 字符串的哈希值
  • 详细检查 = 逐个字符比较
1.2 滚动哈希的原理

这是Rabin-Karp最精妙的部分。我们来看一个具体的例子:

假设我们在文本 "314159" 中搜索 "141",基数为10。

第一轮:计算 “314” 的哈希值

hash = (3 × 10² + 1 × 10¹ + 4 × 10⁰) = 314

第二轮:从 “314” 滑动到 “141”

传统做法是重新计算:(1 × 10² + 4 × 10¹ + 1 × 10⁰) = 141

但滚动哈希这样做:

1. 移除最高位3:314 - 3 × 10² = 14
2. 整体左移:14 × 10 = 140  
3. 添加新位1:140 + 1 = 141

看到了吗?我们只用了3次算术运算,而不是3次乘方运算!

滚动hash

1.3 哈希冲突

这里有个关键问题:不同的字符串可能有相同的哈希值。比如:

  • "ABC" 的哈希可能是 123
  • "XYZ" 的哈希也可能是 123

这就是哈希冲突。Rabin-Karp的解决方案很实在:哈希匹配后,再逐个字符验证

def rabin_karp_detailed(text, pattern):
    n, m = len(text), len(pattern)
    if m == 0: return []
    
    base, modulus = 256, 10**9+7
    
    # 计算最高位权重:base^(m-1) % modulus
    highest_power = 1
    for _ in range(m-1):
        highest_power = (highest_power * base) % modulus
    
    # 计算模式串和第一个窗口的哈希
    pattern_hash = 0
    window_hash = 0
    for i in range(m):
        pattern_hash = (pattern_hash * base + ord(pattern[i])) % modulus
        window_hash = (window_hash * base + ord(text[i])) % modulus
    
    result = []
    
    # 主循环:滑动窗口
    for i in range(n - m + 1):
        # 先比较哈希值
        if pattern_hash == window_hash:
            # 哈希匹配,再逐个字符确认
            match = True
            for j in range(m):
                if text[i+j] != pattern[j]:
                    match = False
                    break
            if match:
                result.append(i)
        
        # 如果不是最后一个窗口,计算下一个窗口的哈希
        if i < n - m:
            # 滚动哈希:移除旧字符,添加新字符
            old_char_value = ord(text[i]) * highest_power
            new_char_value = ord(text[i+m])
            
            window_hash = (window_hash - old_char_value) % modulus
            window_hash = (window_hash * base + new_char_value) % modulus
            
            # 处理负数
            if window_hash < 0:
                window_hash += modulus
    
    return result
1.4 现实生活中的案例:图书馆检索系统

想想图书馆怎么找书:

  1. 先用索引卡(哈希值) 快速找到可能的位置
  2. 再到书架前(逐个比较) 确认是不是想要的书

这样既快又准,Rabin-Karp也是这个思路。


二、 Boyer-Moore算法

2.1 为什么从后往前比较?

大多数人习惯从左往右阅读,但Boyer-Moore告诉我们:有时候反过来更高效!

思考:
"Hello, beautiful World!" 中找 "Word"

如果从左边开始:

Hello, beautiful World!
Word
  ^ 第3个字符就不匹配

如果从右边开始:

Hello, beautiful World!
Word
   ^ 第4个字符'd' vs 'l' 不匹配

发现了吗?从右边开始,我们一次就能获得更多信息

2.2 坏字符规则的底层逻辑

当发现不匹配时,我们问自己:这个"坏字符"在模式串的哪里?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

QuantumLeap丶

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值