如果我们需要统计一个模板串在文本串中出现的次数,该怎么做捏?
首先我们很容易想到暴力,也很容易写出暴力代码
代码如下:
#include <bits/stdc++.h>
using namespace std;
int countOccurrences(const string & text, const string & pattern)
{
int m = pattern.length(),n = text.length(),count = 0;
for (int i=0;i-m<=n;++i){
int j;
for (j=0;j<m;++j) if (text[i+j]!=pattern[j])break;
if (j == m) count++;
}
return count;
}
int main()
{
string text = "abcbabcabcabc";
string pattern = "abc";
int occurrences = countOccurrences(text, pattern);
cout << "模板串在文本串中出现的次数为:" << occurrences << endl;
return 0;
}
运行结果如下:
但是,时间复杂度为O(n*m),很慢,所以我们使用字符串哈希进行比较。
字符串哈希的基本原理就在于,计算机比较字符串慢,比较数字快,所以说将字符串哈希映射成数,再比较数字就更快了。
但是在映射的过程中,会出现冲突的情况,所以我们所设计及的哈希函数就要避免大部分冲突,
(当然还是有很多大佬,出的数据可以卡单双哈希,就不得不寻找其他方法来解题)。
这里我使用的是自然溢出法,就可以不用做取模。
求字符串的哈希值的代码如下:
unsigned long long str_hash(const string & a)
{
unsigned long long ans = a[0];
int len = a.size();
for(int i=1;i<len;i++) ans = ans*P+a[i];
return ans;
}
P 取得一个数,最好是质数,减少哈希冲突。
但是如果是一个字符串我们求它所有前缀的哈希值呢?我们需要用到滚动哈希。
创建一个pre数组来存前缀的哈希值,就类似于前缀合之类的。代码如下:
using ull = unsigned long long;
const int N = 1e6+5;
const int P = 13331;
ull pre[N];
ull p[N];
void strhash(const string & x)
{
int len = x.size();pre[0] = x[0];p[0] = 1;
for(int i=1;i<len;i++){ pre[i] = pre[i-1] * P + x[i] ; p[i] = p[i-1]*P; }
}
当然p数组可以用pow函数来解决,或快速幂。相信作为大佬的你已经看懂了这个程序。
还有一个问题就是我如果想求字串而并非前缀的哈希值呢?
假设我们的哈希函数可以把“12345”这个字符串转换成整数12345,对应的“123”就是123,那我要求“234”这个字串的哈希值就是234,如下图:
可得程序:
ull get_sonstr(int l,int r)
{
if(l==0) return pre[r];
return pre[r] - pre[l-1] * p[r-l+1];
}
当l==0时就是求前缀,需要单独处理。
那么哈希的三个基本函数作为大佬的你一定理解了吧!
去练习一下吧!