两种简单的字符串匹配算法

在机试笔记4中的统计子字符串个数题目中,使用了一种时间复杂度为O(n*m)的字符串匹配算法,他也叫暴力匹配算法或者BF算法。在实际的开发中,它却是一个比较常用的字符串匹配算法。原因有以下几点:

第一,实际的软件开发中,大部分情况下,模式串和主串的长度都不会太长。而且每次模式串与主串中的子串匹配的时候,当中途遇到不能匹配的字符的时候,就可以就停止了,不需要把 m 个字符都比对一下。所以,尽管理论上的最坏情况时间复杂度是 O(n*m),但是,统计意义上,大部分情况下,算法执行效率要比这个高很多。

第二,朴素字符串匹配算法思想简单,代码实现也非常简单。简单意味着不容易出错,如果有 bug 也容易暴露和修复。在工程中,在满足性能要求的前提下,简单是首选。这也是我们常说的KISS(Keep it Simple and Stupid)设计原则。

所以,在实际的软件开发中,绝大部分情况下,朴素的字符串匹配算法就够用了。

一个长为n的主串中去查找长为m的模式串,最多要对比n-m+1个子串与模式串。

另一个字符串匹配算法叫RK算法,它的算法思想是:

我们通过哈希算法对主串中的 n-m+1 个子串分别求哈希值,然后逐个与模式串的哈希值比较大小。如果某个子串的哈希值与模式串相等,那就说明对应的子串和模式串匹配了。这里当然会有哈希冲突的可能。

哈希函数的设计:

    假设字符串只有小写字母,可以把每个子字符串当成一个26进制数,哈希函数就是把这个26进制数转成10进制数。

代码如下:

#include <bits/stdc++.h>
using namespace std;
double my_hash(string str)
{
    double sum = 0;
    int j=0;
    for(int i=str.size();i>=0;i--){
        double index = pow(26,j);
        sum+=(str[i]-'a')*index;
        j++;
    }
    return sum;
}

int main()
{
    int n;
    int m;
    string  a,b;
    cin >> n>> m;
    getchar();
    getline(cin,a);
    getline(cin,b);
    double* arr = new double[n-m+1];
    for(int i=0;i<n-m+1;i++){
        string c = a.substr(i,m);
        arr[i]=my_hash(c);
    }
    double v = my_hash(b);
    int sum = 0;
    for(int i=0;i<n-m+1;i++){
        if(arr[i]==v)
            sum++;
    }
    cout << sum<< endl;
    return 0;
}

 当然这个代码还有需要优化的地方,一是计算指数可以变成查表的方式,二是求其十进制时前后两个子串的计算是有交集的。

posted @ 2020-04-05 19:13  不二良  阅读(515)  评论(0编辑  收藏  举报