Rabin-Karp-MATCHER字符串匹配算法; 一种效率还不错的匹配算法; 思想是关键.

#include <stdio.h> #include <string.h> /*********************************/ * n:文本串T长度 * m:模式串P长度 * h:d^(m-1) * d:进制基数(这里采用26进制) * t[s]:对应位移s的文本串T子串的d进制表示 * p:模式串P的d进制表示 * q:对d进制整数模q,q是素数(越大越容易排出非法位移s,但是过大会导致模运算溢出,这里取13) /*********************************/ const int MAXN = 10000; int n,m,h,p,d,q,t[MAXN]; char T[MAXN],P[MAXN]; /*********************************/ * RabinKarpMatcher()串匹配算法 * 预处理时间:O(n-m+1)+O(m) * 匹配时间最坏:O((n-m+1)*m) * 但实际运行效果一般比这个好 * 预处理:将T[s]与P都转化成d进制整数 * 匹配问题就变成了判断整数T[s]=P * 由于整数可能很大,所以必须作Mod运算 * 这样就导致即便Mod后T[s]=p * 也不能确定是否真的完全相等 * 所以,需要检测这个T[s]是否是伪命中 * 如果Mod后都不相等,则肯定非命中 /*********************************/ int modPow(int x,int y,int z) { int ret=1,i=0; for(;i<y;++i) { ret=(int )( (ret*(long long)x) % z ); // 严格要求 q * d 在一个机器字长内, 即C语言里long long 的64 bits } return ret; } void dNum() { int i=1; p=0; t[0]=0; for(;i<=m;++i) { p= (int ) (( (long long )p*d+(P[i]-'a') ) %d ); t[0]= (int) (((long long)t[0]*d+(T[i]-'a') )%d); } } bool RabinKarpMatcher() { d=26; //26进制数 q=13; //模13 n=strlen(T)-1; m=strlen(P)-1; h=modPow(d,m-1,q); //计算d^(m-1) mod q dNum(); int s,j; bool has=false; for(s=0;s<=n-m;++s) { if(t[s]==p) { bool is=true; for(j=1;j<=m;++j) { if(T[s+j]!=P[j]) { is=false; break; } } if(is) { printf("T中有效位置:%d/n",s+1); has=true; } } if(s<n-m) { // +26*q防止出现运算过程中的负数 t[s+1]=( int ) ( ( d* ( t[s]- ( (T[s+1]-'a') * (long long )h )+ 26*q ) + ( T[s+m+1]-'a') ) %q ); } } return has; } int main() { T[0]=P[0]='z'; printf("输入文本串T,模式串S,以空格/回车作为间隔./n"); scanf("%s%s",T+1,P+1); if(!RabinKarpMatcher()) { printf("对不起,没有匹配成功!/n"); } return 0; }

 

 

描述一下算法的思想:

 

朴素的字符串匹配, 是对文本串T的每一位开始的子串与模式串P进行匹配, 一共有n-m+1位, 每次最坏扫描m位, 所以复杂度O(n(n-m+1))

 

RabinKarp算法是怎么优化这个过程的呢?

 

首先, 讲朴素的字符串匹配变的更简单一些, 把从s位开始的T子串转化为一个整数, 把P串也转化成一个整数, 只要t[s]=p ,即两个串对应的整数相等, 很明显两个串也就匹配了, 举个例子就懂了;

 

假设字符串里只有a,b,c三个字符, 对应的10进制数为: a:0, b:1, c:2;

 

假设文本串T:abc

假设模式串P: ab

 

那么先将P串转化成整数就是0*10+1=1

文本串T的子串转化成整数分别是: T[0]=0*10+1=1;  T[1]=1*10+2=12;

 

其实就是把每个字符对应一个d进制数的1位, 然后像二进制一样, 求出它对应的整数 ,这里转化有个迭代运算方法,比较方便,叫做霍纳法则,可以查一下,代码里也有体现.

 

现在匹配变成了判断T[s]==p ,那么就匹配成功.

 

但是, 如果串P很长, 那么整数会很大很大, 会溢出, 所以必须做一些处理, 就是MOD运算.

 

两个不同的串对应的整数MOD后也可能相等,这样判断起来就没啥意思了, 但是有一个特性, 如果两个串对应的整数的MOD结果不同,那么这两个串不可能相同!

 

所以, 利用这种方法, 可以在朴素的匹配方法的基础上, 利用这个性质直接排除一些位置的判断 ,这样就减小了复杂度.

 

预处理就是求t[s]与p, 单独求每个t[s] 复杂度就是平方级了, 所以这里有一个巧妙的递推方法求所有的t[s].

 

举个例子, 不讲公式了.

 

假设文本串T:abc

假设模式串P: ab

 

T[0]= ab , 对应01

T[1]= (01 - 0*10) *10 +2 =12

 

就是本来ab对应01 , 把a的0去掉,在1的右边加上c=2, 变成12 ,这个过程是可以利用不同进制间的转化的原理用算式算出来的.

 

 

就讲这些,看算法导论应该可以轻松看懂,很简单.

你可能感兴趣的:(c,算法,优化,语言,include)