字符串匹配

来源:互联网 发布:php免费源码下载 编辑:程序博客网 时间:2024/06/01 07:14

一、问题定义

       已知一个源字符串T,和一个目标字符串P,其中T的长度大于等于P的长度,求T中与P完全相同的子串的位置。

       例:已知T=” abbabcabbababca” ,P=”abca”则结果为3和12

二、问题求解

       下面介绍了解决该问题的四种方法,即朴素算法、Rabin_Karp算法、有限自动机算法、KMP算法。一下算法中T的长度为n,P的长度为m。

1.      朴素算法

(1)      基本思想

(a) 将T分解成n-m+1个字符串;

(b) 分别与P中的字符逐一的比较,记录完全相等的字符串;

(c) 返回所有完全相等的字符串的起始位置。

(2)      主代码

       int pos = 0;

       for (i=0; i<n-m+1; ++i)

{

       for (j=0; j<m; ++j)

       {

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

       break;

}

if (j == m)

{

       s[pos++] = i;

}

}

                     s集合中所有的数据即为所求结果。

(3)      时间复杂度

       每一个字符串的匹配最长时间为m,因此最坏时间复杂度O((n-m+1)m)

2.      Rabin_Karp算法

(1)      基本思想

                                     i.             将目标字符串P映射成一个数值p;

                                   ii.             将源字符串T映射成n-m+1个数值t1, t2, ……,tn-m+1

                                 iii.             若p=tq,则tq所在的第一个位置为正确结果。

(2)      转换方法

                                     i.             扫描源字符串找出字符串中包含的不同字符串的个数d;

                                   ii.             p=dm-1P[1]+dm-2P[2]+…+dP[m-1]+P[m]

                                 iii.             t0=dm-1T[1]+dm-2T[2]+...+dT[m-1]+T[m]

(3)      主代码

int str_switch_to_int(CStringstr, int n,int m)

{

         int temp = 0;

         for (int i=n; i<m+n; ++i)

{

       temp = temp*10 + str[i];

}

return temp;

}

void match()

{

         p = str_switch_to_int(P,0,m);

         int pos = 0;

         for (int i=0; i<n-m+1; ++i)

{

       t[i] =str_switch_to_int(T,i,m);

}

for (int i=0; i<n-m+1; ++i)

{

       if (p == t[i])

       {

              s[pos++] = i;

}

}

}

s集合中所有的数据即为所求结果。

(4)      时间复杂度

从match函数易得时间复杂度为O(n-m+1)。

3.      有限自动机算法

(1)      基本思想

                                     i.             对目标字符串构造出一个字符串匹配的有限自动机;

                                   ii.             对源字符串进行扫描;

                                 iii.             当自动机的状态变为A中的一个元素时产生一个结果。

(2)      有限自动机

                                     i.             定义:M=(Q,q0,A,∑,δ)其中,Q表示状态的有限集合,q0表示出初始状态,A输出状态集合,∑目标字符串的字符集,δ表示状态转移函数。

                                   ii.             例:∑=abcd;

则有四种状态,分别为q0=∅,q1={a},q2={ab},q3={abc}, q4={abcd};

初始状态q0=∅;

状态的集合Q={q0,q1,q2,q3,q4};

输出的状态集A={q4}

状态转移函数δ(q0,a) = q1, δ(q1,b) = q2, δ(q2,c)= q3, δ(q3,d) = q4

(3)      构造自动机方法

构造自动机最困难的就是确定状态转移函数,即求解δ(qp,x) = ?,其中x为字符集∑中的任意一个字符,qp为状态集中的任意一种状态,下面举例描述自动机状态转移函数的求解方法。

                                     i.             ∑=abc,q0=∅,q1={a},q2={ab},q3={abc}

                                   ii.             δ(q0,a)= q1,δ(q0,b) = q0,δ(q0,c) = q0

                                 iii.             δ(q1,a)= q1,δ(q1,b) = q2,δ(q1,c) = q0

                                  iv.             δ(q2,a)= q0,δ(q2,b) = q0,δ(q2,c) = q3

                                    v.             δ(q3,a)= q1,δ(q3,b) = q0,δ(q3,c) = q0

则得到表

 

输入

状态

a

b

c

0

1

0

0

1

0

2

 

2

0

0

3

3

1

0

0

              自动机的求解过程称为预处理,易得预处理时间复杂度为O(m3|∑|)。

(4)      时间复杂度

匹配只需从前到后扫描一遍源字符串T即可,因此时间复杂度O(n),而预处理时间复杂度是O(m3|∑|)。

(5)      优缺点

优点:源字符串不回溯,匹配时间短

缺点:构造自动机代价大

4.      KMP算法

(1)      KMP是三位设计人员Knuth、Morris、Pratt的首字母的组合。

(2)      主代码

                                     i.             void KMP_getNext()

{

       int i,q = 0;

       next[1] = 0;

       for (i=2; i<=plenth;++i)

{

       while ((q>0)&&(p[q+1]!=p[i]))

              q = next[q];

       if (p[q+1]==p[i]) q++;

       next[i] = q;

}

}

voidKMP_match()

{

     int i,q=0;

     int pos=0;

     for (i=1; i<=tlength; ++i)

{

       while((q>0)&&(p[q+1]!=t[i]))

              q=next[q];

       if (p[q+1]==p[i]) q++;

       if (q == plength)

       {

       q=next[q];

       s[pos++] = i-plength;

}

}

}

(3)      时间复杂度

预处理时间O(m),匹配时间O(n)

三、小结

 

各算法时间复杂度比较

 

 

预处理时间

匹配时间

朴素算法

 

O((n-m+1)m) 

Rabin_Karp算法

O(m) 

O(n+m) 

有限自动机算法

 O(m3|∑|)

 O(n)

KMP算法

 O(m)

O(n) 

原创粉丝点击