转自 http://blog.csdn.net/touzani/archive/2007/05/30/1632149.aspx
字符串匹配(String matching)
算法
|
预处理时间
|
匹配时间
|
|
||
朴素算法
|
0
|
O ((
n -
m + 1)
m )
|
Rabin-Karp
|
Θ(
m )
|
O ((
n -
m + 1)
m )
|
有限自动机算法
|
O (
m |Σ|)
|
Θ(
n )
|
KMP算法
|
Θ(
m )
|
Θ(
n )
|
如果某个字符串 y ∈ Σ*,使得x=wy 。则称w是x的前缀, 记为w � x 。 如果w是x的后缀,记为w � x
可以把字符串匹配问题描述为 找出0 ≤ s ≤ n -m 并满足P � Ts +m 的所有位移s
因此,字符串"31415" 对应于十进制数31415
已知模式P[1..m],设p表示其相应十进制数地值,类似地, 对于给定的文本T[1..n]. 用
可以用霍纳规则(Horner’s rule) 在Θ(m ) 的时间内计算p的值
p = P [m ] + 10 (P [m - 1] + 10(P [m - 2] + · · · + 10(P [2] + 10P [1]) )).
如果能在总共Θ(n - m + 1) 时间内计算出所有的ts 的值,那么通过把p值与每个ts (有n-m+1个)进行比较,就能够在Θ(m ) + Θ(n - m + 1)= Θ(n ) 时间内求出所有有效位移。(计算出1个ts 就跟p比较,处理结果。)
为了在Θ(n - m ) 时间内计算出剩余的值t 1 , t 2 , . . . , 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
因此,可以在Θ(m )时间内计算出p和t 0 。 然后在Θ(n - m + 1)时间内计算出t 1 , . . . , 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, 使其为:
但是加入模q后,由ts ≡ p (mod q )不能说明 ts = p . 但ts � p (mod q ), 可以说明 ts ≠ p ,
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 t
0
← 0
6 for
i
← 1 to
m
� Preprocessing.
7 do
p
← (dp
+ P
[i
]) mod q
8 t
0
← (dt
0
+ 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]) mod q
实际应用中,有效位移数很少(常数c个),因此期望的匹配时间为O ((n - m + 1) + cm ) = O (n +m ), 选取的素数q比p的长度m大得多。
通常处理ASCII码字符, d=128, 素数q可选6999997。
//*****************************************************************************************************************************
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#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<m) return -2;
int i,s,t=0,p=0,h=1;
for(i=1; i<m; i++) h=(h*d) % q; //h=d^m-1
for(i=0,p,t; i<m; 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<m; i++)
if(pat[i] != T[s+i]) break;
if(i==m) return s;
}
if(s<n-m)
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;
}