Rabin-Karp算法
用途
此算法主要用来解决字串匹配问题,例如设s为一个字符串,s1为一个字串,我们的目的是判断s中是否存在s1,也可以判断有多少s1字串
解决
- 对于此问题的解决,可以采用暴力解法,即从s中逐个字串的查找,然后判断是否存在,其优点是不需要对字串进行预处理,缺点是时间复杂度太高O(
MN
) - 采用Rabin-Karp算法,该算法的主要思想是采用哈希,把字符串计算成一个哈希值,然后判断是否相等
Rabin-Karp算法具体实现
我们假设字符串中只包含小写字符,则进制为26进制,我们设字串中字符的个数为m
,则字串的哈希值的计算为:
hash(s1)=s1[0]*dm-1 *+s1[1]dm-2+…+s1[m-1] (其中d为进制数)
同理,对字符串s(设字符的个数为n
)也采用此算法进行计算,s能够截取的字串一共有m-n+1个可能。对于每一个字串我们无需重新计算,可以利用上一步的结果进行计算,例如我们设划片的哈希值为hash(s[0]) 表示从0开始截取的字串的哈希值,则hash(s[1])=d(hash(s[0])-s[0]*dm-1 )+s[n]
此外如果哈希值过大我们也可以采用求模运算缩小哈希值,但带来的缺点是会造成哈希冲突。
具体问题:
所有 DNA 都由一系列缩写为 ‘A’,‘C’,‘G’ 和 ‘T’ 的核苷酸组成,
例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。编写一个函数来找出所有目标子串,目标子串的长度为 10,且在 DNA 字符串 s 中出现次数超过一次示例 1:
输入:s = “AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT”
输出:[“AAAAACCCCC”,“CCCCCAAAAA”]
问题分析
此问题即字符串匹配问题,我们可以设A=0,C=1,G=2,T=3即四进制的数,然后每取10个字串计算一次哈希值即可
代码实现
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
/*unordered_map<string,int> m;
vector<string> res;
int size=s.size();
int start=0;
string s1;
while(start<=size-10){
s1=s.substr(start,10);
if(m[s1]==1) res.emplace_back(s1);
m[s1]++;
start++;
}
return res;*/
//不取余数的情况下不会有冲突 取余数则可能冲突
int n=s.size(),d=4,m=10;
vector<string> res;
map<char,int> f;
map<int,int> hash; //用来保存哈希值
f['A']=0; f['C']=1; f['G']=2;f['T']=3;
int h=0;
for(int i=0;i<n-m+1;i++){
h=0;
for(int j=0;j<m;j++){
h=h*d+f[s[i+j]];
}
if(hash[h]==1){
res.emplace_back(s.substr(i,m));
}
hash[h]++;
}
return res;
}
};