Rabin-Karp算法 java_字符串匹配算法之Rabin-Karp算法(转)

NAIVE-STRING-MATCHER(T, P)

1 n ← length[T]

2 m ← length[P]

3 for s ← 0 to n - m

4     do if P[1 ‥ m] = T[s + 1 ‥ s + m]         // 隐含着一个循环

5           then print "Pattern occurs with shift" s

时间复杂度为O((n - m + 1)m), 如果m = �n/2�. 那么时间复杂度为Θ(n2), 这个算法效率不高,原因在于对于s的一个值,获得的关于文本的信息在考虑s的其他值时完全被忽略了。

例如,如果 P=aaab,设s=0 是有效的,那么 s=1, 2, 3 就不可能是有效位移,因为T[4]=b.

2.) Rabin-Karp字符串匹配算法,

实际应用中,Rabin和Karp建议的字符串匹配算法能较好地运行,还可以归纳出有关问题地其他算法,如二维模式匹配。

假定字符集Σ ={0, 1, 2, ……, 9}, 每一个字符对应一个十进制数字

(一般情况, 假定每个字符是基数为d的表示法中的一个数字, d=|Σ|。)可以用一个长度为k的十进制数字来表示由k个连续字符组成的字符串.

因此,字符串"31415" 对应于十进制数31415

已知模式P[1..m],设p表示其相应十进制数地值,类似地, 对于给定的文本T[1..n]. 用

ts 表示长度为m的子字符串 T[s + 1 ‥ s + m]( s = 0, 1, . . . , n – m), ts = p 当且仅当 [s + 1 ‥ s + m] = P[1 ‥ m]; 因此s是有效位移当且仅当 ts = p. (暂不考虑p和ts可能是很大的数的情况)。

可以用霍纳规则(Horner’s rule) 在Θ(m) 的时间内计算p的值

p = P[m] + 10 (P[m - 1] + 10(P[m - 2] + · · · + 10(P[2] + 10P[1]) )).

Horner’s rule

类似地,可以在Θ(m)时间内,根据T[1..m]计算出t0的值。

如果能在总共Θ(n - m + 1) 时间内计算出所有的ts的值,那么通过把p值与每个ts(有n-m+1个)进行比较,就能够在Θ(m) + Θ(n - m + 1)= Θ(n) 时间内求出所有有效位移。(计算出1个ts就跟p比较,处理结果。)

为了在Θ(n - m) 时间内计算出剩余的值t1, t2, . . . , tn-m可以在常数的时间内根据ts计算出ts+1,先看例子,假如m = 5,ts = 31415, 我们去掉高位数字T [s + 1] = 3,然后在加入一个低位数字T [s + 5 + 1](假设为2),得到:

ts+1 = 10(31415 - 10000 • 3) + 2 = 14152.

总结出公式:      ——公式1

如果预先计算出10m-1(通过数论中的技术可以在O(lg m)完成, 在这里只需简单地在O(m)时间内计算出就可以)。那么就可以在常数时间计算出ts+1

因此,可以在Θ(m)时间内计算出p和t0。然后在Θ(n - m + 1)时间内计算出t1, . . . , tn-m 并完成匹配。

现在来解决唯一的问题,就是计算中p和ts的值可能太大,超出计算机字长,不能方便地进行处理。如果p包含m个字符,那么, 关于在p上地每次算术运算需要“常数”时间这一假设就不合理了,幸运的是,对这一问题存在一个简单的补救方法,对一个合适的模q来计算p和ts的模,每个字符是一个十进制数,因为p和t0以及公式1计算过程都可以对模q进行,所以可以在Θ(m)时间内计算出模q的p值,在Θ(n - m + 1)时间内计算出模q的所有ts值,通常选模q为一个素数,使得10q正好为一个计算机字长,单精度算术运算就可以执行所有必要的运算过程。 一般情况下,采用d进制的字母表{0, 1, . . . , d - 1}, 所选的q要满足d*q < 字长,调整公式1, 使其为:

其中的h = d m-1 (mod q)

但是加入模q后,由ts ≡ p (mod q)不能说明ts = p. 但ts � p (mod q), 可以说明 ts ≠ p,

因此当ts ≡ p (mod q)时, 再用朴素的字符串匹配算法验证ts = p。. 如果q足够大,可以期望伪命中很少出现。

算法

RABIN-KARP-MATCHER(T, P, d, q)

1 n ← length[T]

2 m ← length[P]

3 h ← dm-1 mod q

4 p ← 0

5 t0 ← 0

6 for i ← 1 to m           � Preprocessing.

7     do p ← (dp + P[i]) mod q

8        t0 ← (dt0 + T[i]) mod q

9 for s ← 0 to n - m       � Matching.

10     do if p = ts

11           then if P[1 ‥ m] = T [s + 1 ‥ s + m]

12                   then print "Pattern occurs with shift" s

13        if s < n - m

14           then ts+1 ← (d(ts - T[s + 1]h) + T[s + m + 1]) modq

c++代码

void RABIN_KARP_MATCHER(string T, string P, int d, int q)

/*

搜索P在T中出现的位置

参数d :字母表的进制,亦即是字母表的元素个数

参数q : 一个较大的素数, 只需d*q 

*/

{

int n= T.length();

int m= P.length();

if( n 

return ;

int i, h=1;

for(i=1; i<=m-1; i++)   // caculate h

h = h*d%q;

int p=0, t=0;

for(i=0; i

{

p = (( d*p + P[i]) % q);

t = (( d*t + T[i]) % q);

}

int s;

for(s=0; s 

{

if( p == t )

{

for(i=0; i

if(P[i]!=T[s+i])

break;

if(i==m)

cout<

}

if( s 

t= (  d* (t - T[s]*h%q + q) + T[s+m])  % q;  // 计算ts+1

}

cout<

return  ;

}

RABIN_KARP_MATCHER预处理时间为Θ(m) 匹配时间最坏情况下为Θ((n - m + 1)m),

因为Rabin_Karp算法跟朴素的字符串匹配算法一样,对每个有效位移进行显示验证,如果P = am and T = an, 则验证所需时间为Θ((n - m + 1)m), 因为n - m + 1个可能的位移中每一个都是有效位移。

实际应用中,有效位移数很少(常数c个),因此期望的匹配时间为O((n - m + 1) + cm) = O(n+m), 选取的素数q比p的长度m大得多。

通常处理ASCII码字符, d=128, 素数q可选6999997。

//*****************************************************************************************************************************

#include

#include

#include

#define MAXN 100

const int d=128;

const int q=6999997;

int RK(char *T,char *pat)

{

int n=strlen(T),m=strlen(pat);

if(n

int i,s,t=0,p=0,h=1;

for(i=1; i

for(i=0,p,t; i

{

p = ( d*p+pat[i] ) % q; //p[m]

t = ( d*t+T[i] ) % q; //t[m]

}

for(s=0; s<=n-m; s++)

{

if(p==t)

{

for(i=0; i

if(pat[i] != T[s+i]) break;

if(i==m) return s;

}

if(s

t = ( d*(t-h*T[s]) + T[s+m] ) % q; //t[s+1]

}

return -1;

}

char s1[MAXN],s2[MAXN];

int main()

{

while(scanf("%s%s",s1,s2))

{

printf("%d\n",RK(s1,s2));

}

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值