使用递归哈希进行精确模式串匹配

简介: 字符串处理是每个编程者都必须掌握的知识,主要看看字符串的搜索查找功能。 现在的编程语言如C/C++/Java等都提供了对字符串子串的查找功能,具体如下: (1)C:strchr,strstr。 (2)C++:find,rfind,find_first_of,find_first_not_of等等。

字符串处理是每个编程者都必须掌握的知识,主要看看字符串的搜索查找功能。

现在的编程语言如C/C++/Java等都提供了对字符串子串的查找功能,具体如下:

(1)C:strchr,strstr。

(2)C++:find,rfind,find_first_of,find_first_not_of等等。

(3)Java:indexOf,lastIndexOf等。

 

下面说明一种使用递归哈希进行字符串搜索/查找的方法:

(1)递归哈希

  维护一个窗口,大小为n。如下公式即为起始位置为x,长度为n的窗口的哈希数值。

  递归哈希主要体现在哈希数值的更新操作,减少重复的计算。下面是递归哈希的更新公式。

  

因为窗口[x,x+n)与[x+1,x+n+1)有n个相同的字符,在上的更新公式中我们可以看到,更新就是把“头元素”的哈希数值去掉,在加上一个新增的窗口元素。

(2)使用递归哈希进行字符串匹配

  设模式串为pattern,文本为text,设定窗口大小Plen与模式串的长度相同。每次保持文本中Plen长度字符串的哈希值,当哈希值与模式串的哈希值相等时,进行字符串的具体校验。如果校验相等,报告结果。

 1 /*
 2 使用递归哈希进行字符串精确匹配
 3 */
 4 #include<iostream>
 5 #include <string>
 6 #define Q 13
 7 #define MOD 5549873
 8 using namespace std;
 9 
10 /*
11 初始化哈希值
12 str:要计算哈希数值的字符串
13 */
14 unsigned long recHash(const string &str){
15     unsigned long hash =0;
16     for(int i=0;i<str.size();i++)
17         hash=(hash*Q+str[i])%MOD;
18     return hash%MOD;
19 }
20 
21 /*
22 校验阶段
23 text:带匹配文本
24 pattern:模式
25 pos:模式在文本中最后一个字符的位置
26 */
27 int isverify(const string &text,const string &pattern,int pos){
28     string sub_str=text.substr(pos-pattern.size()+1,pattern.size());
29     if(sub_str == pattern){
30         cout<<"模式位置:"<<pos-pattern.size()+1<<endl;
31         return 1;
32     }
33     return 0;
34 }
35 
36 /*
37 text:带匹配文本
38 pattern:模式
39 Phash:模式对应的哈希值
40 */
41 int match(const string &text,const string &pattern,const unsigned long Phash){
42     int Tlen=text.size();
43     int Plen=pattern.size();
44     bool tag=false;
45     if(Tlen<Plen){
46         cout<<"文本中没有模式存在!!!"<<endl;
47         return 0;
48     }
49 
50     unsigned long exp=1;
51     for(int i=0;i<Plen-1;i++)
52         exp=(exp*Q)%MOD;
53 
54     unsigned long Thash=recHash(text.substr(0,Plen));
55     if(Thash == Phash && isverify(text,pattern,Plen-1))
56         tag=true;
57 
58     int pos=Plen;
59     while(pos<Tlen){
60         Thash=((Thash+MOD-(text[pos-Plen]*exp)%MOD)%MOD*Q+text[pos])%MOD;
61         if(Thash == Phash && isverify(text,pattern,pos))
62             tag=true;
63         pos++;
64     }
65 
66     if(!tag)
67         cout<<"文本中没有模式存在!!!"<<endl;
68     return 0;
69 }
70 
71 int main(){
72     string pattern;
73     string text;
74     while(true){
75         cout<<"要查找的模式:"<<endl;
76         cin>>pattern;
77 
78         cout<<"待匹配的文本:"<<endl;
79         cin>>text;
80 
81         cout<<"结果:"<<endl;
82         match(text,pattern,recHash(pattern));
83         cout<<endl;
84     }
85     return 0;
86 }
View Code

执行输出:

 

相关文章
|
2月前
|
人工智能 自然语言处理 算法
【动态规划】【字符串】【前缀和】1639通过给定词典构造目标字符串的方案数
【动态规划】【字符串】【前缀和】1639通过给定词典构造目标字符串的方案数
|
4月前
|
算法 测试技术 C#
C++单调向量算法:得到山形数组的最少删除次数
C++单调向量算法:得到山形数组的最少删除次数
|
存储 索引
【题型总结】使用双指针+哈希表寻找符合某种条件的字符串/数字的个数
记录第一出现的字符的位置和最后一个出现的字符的位置,如果相减长度为s1的长度,那么证明是连续出现的子串
67 0
|
算法 Java
(Rabin-Karp算法)匹配字符串(滚动哈希)
Rabin-Karp 算法用于多模式搜索,常用于重复检测和生物信息学中寻找两个或多个蛋白质的相似性。
177 0
(Rabin-Karp算法)匹配字符串(滚动哈希)
|
存储 算法 容器
Leetcode 76最小覆盖子串&77组合&78子集
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
90 0
Leetcode 76最小覆盖子串&77组合&78子集
回溯——77. 组合
本专栏按照数组—链表—哈希—字符串—栈与队列—二叉树—回溯—贪心—动态规划—单调栈的顺序刷题,采用代码随想录所给的刷题顺序,一个正确的刷题顺序对算法学习是非常重要的,希望对大家有帮助
回溯——77. 组合
寻找旋转排序数组中的最小值 (重复与非重复代码)
寻找旋转排序数组中的最小值 (重复与非重复代码)
91 0
寻找旋转排序数组中的最小值 (重复与非重复代码)