字符串匹配----哈希算法

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/OscaronMar/article/details/80094252

寻找字符串S中字符串T出现的位置或次数的问题属于字符串匹配问题。我们接下来讨论中假设S的字符串长度为 n,T的长度为m。最朴素的想法是,枚举所有起始位置,再直接检查 是否匹配,复杂度为O(nm)的算法。还有几个为高效的算法。而在此我们只介绍实现起来较为容易,而在一些稍作变化的问题中同样适用,并且可以简单地推广到二维情况的哈希算法。
将哈希算法用于字符串匹配的原理非常简单。对于每个起始位置,我们不是O(m)地直接比较字符串是否匹配,而是O(l)地比较长度为m的字符串子串地哈希值与T地哈希值是否相等。虽然即使哈希值相等也未必相等,但如果哈希值是随机分布地话,不同的字符串哈希值相等的概率是很低的,可以当作这种情况不会发生。
但是,如果我们采用O(m)的算法计算长度为 m地字符串子串地哈希值的话,那复杂度还是O(nm)。这里我们要使用一个叫做滚动哈希的优化技巧。选取两个合适的互素常数b和h(l

#include<string>
#include<iostream>
using namespace std;
typedef unsigned long long ull;
const ull B = 100000007;//哈希的基数
//a 是否在 b中出现
bool contain(string a,string b){
    int al = a.length(),bl = b.length();
    if(al > bl)
        return false;

    //计算 B的 al次方 
    ull t = 1;
    for(int i=0;i<al;i++)
        t *= B;
    //计算 a和 b的长度为 al的前缀对应的哈希值
    ull ah = 0,bh = 0;
    for(int i=0;i<al;i++){
        ah = ah * B + a[i];
    } 
    for(int i=0;i<al;i++){
        bh = bh * B + b[i]; 
    }   

    //对 b不断右移一位,更新哈希值并判断
    for(int i=0;i + al <= bl;i++){
        if(ah == bh)
            return true;// b从位置 i开始长度为 al的字符串字串等于 a
        if(i + al < bl)
            bh = bh * B + b[i+al] - b[i] * t; 
    } 
    return false;
} 

当然不光是右移一位,对于左移一位、左端或右端加长一位或是缩短一位的情况,也能够进行类似处理。譬如说,假设要求S的后缀和T的前缀相等地最大长度,可以利用滚动哈希在O(n+m)的时间内高效地求得。

#include<string>
#include<iostream>
using namespace std;
typedef unsigned long long ull;
const ull B = 100000007;//哈希的基数

// a的后缀和 b的前缀相等的最大长度
int overlap(string a,string b){
    int al = a.length(),bl = b.length();
    int ans = 0;
    ull ah = 0,bh = 0,t = 1;
    for(int i=1;i<=min(al,bl);i++){
        ah = ah + a[al - i] * t;// a的长度为 i的后缀的哈希值
        bh = bh * B + b[i-1];// b的长度为 i的前缀的哈希值
        if(ah == bh)
            ans = i;
        t *= B; 
    } 
    return ans;
} 
展开阅读全文

没有更多推荐了,返回首页