字符串的模式匹配

      定义:

      主串:"S1S2S3.............Sn” 

      模式串:   "P1P2P3................Pm"

      针对字符串的模式匹配根本思想是在匹配过程中产生“失配”,模式串“向右滑动”的可行距离多远的问题.换句话说也就是,当匹配过程中产生“失配”时,主串中等i个字符(i指针不回溯)应与模式串中哪个字符再比较?

      假设此时应与模式中第k(k < j)个字符继续比较,则模式中前k-1个字符的子串必须满足下列关系式,且不可能存在k‘ >k满足下列关系式

    "p1p2p3...p k-1" = "S i-k+1 S i-k+2......S i-1"            (1)

而已经得到的“部分匹配”的结果是

   "p j-k+1 p j-k+2...p j-1 " =  "S i-k+1 S i-k+2......S i-1(2)

    "p1p2p3...p k-1"  =   "p j-k+1 p j-k+2...p j-1 "            (3)

   反之,若模式串中存在满足(3)式的两个子串,则当匹配过程中,主串中第i个字符与模式中第j个字符比较不等时,仅需将模式向右滑动至模式中第k个字符和主串中第i个字符对齐,此时,模式中有k-1个字符的子串  "p1p2p3...p k-1"必定与主串中第i个字符之前长度为k-1的子串 "S i-k+1 S i-k+2......S i-1"  相等,由此匹配仅需从模式中第k个字符与主串中第i个字符比较继续进行。

 

若令next[j] = k,则next[j]表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置。由此可引出模式中的next函数的定义。

     

          当 j =1时 next[j] = -1;

          当 集合Max{k| 1 < k < j 且 "p1p2p3...p k-1"  = "p j-k+1 p j-k+2...p j-1 " }不为空时,next[j]  =Max

          其它情况  next[j] = 0;

 

 举例说明 按字符存储形式得到的数据

            j          1 2 3 4 5 6 7 8

           模式串    a b a a b c a c

           next[j]  -1 0 0 1 1 2 0 1

KMP算法如下,       C#编写的

   static int index_KMP(string s, string t, int pos)
        {

           //默认的退后位置

           int[] next = { -1, 0, 0, 1, 1, 2, 0, 1 };
            int i = pos;
            int j = 0;
            while (i < s.Length && j < t.Length)
            {
                if ((j == -1) || (s[i] == t[j]))
                {
                   i++;
                   j++;
                }
                else
                    j = next[j];
            }
            if (j >= t.Length)
                return i - t.Length;
            else
                return 0;

        }

 

 

   //测试程序如下 

 static void Main(string[] args)
     {
            string modeStr = "abaabcac";
            string mainStr = "acabaabaabcacaabc";
            int firstPos = 0;
            Console.WriteLine(index_KMP(mainStr,modeStr,firstPos));
            Console.ReadKey();
      }

 

    打印的索引为5。

 

     KMP算法的基础是在已知模式串的next函数的基础上执行的,下面介绍如何对模式串求出Next[j]数组。此函数值取决于模式串本身而和相匹配的主串无关,我们可从分析其定义出发递推的方法求的next函数值。

    由定义可知 next[0] = -1;

设next[j] =k,这表明在模式串中存在下列关系:

   "p1p2p3...p k-1"  = "p j-k+1 p j-k+2...p j-1 " ,其中k 为满足 1 < k < j的某个值,并且不可能存在k' > k 满足等式 (3),此时next[j+1] = ? 可能情况有两种

   (1)若pk = pj,则表明在模式串中

        "p1p2p3...p k"  = "p j-k+1 p j-k+2...p j"                                       (4)

  并且不可能存在k' > k满足等式(4),这就是说next[j+1] = k+1;  即next[j+1] = next[j] + 1;

  (2)若pk != pj  ,则表明在模式串中

            "p1p2p3...p k"  != "p j-k+1 p j-k+2...p j" ,

此时可把求next函数值的问题看成一个模式匹配问题,整个模式串既是主串又是模式串,而当前在匹配的过程中,已有 p j-k+1 =p1, p j-k+2 = p2。。。p j-1 = p k-1 ,则当pk != pj        时应将模式向右滑动至以模式中的第Next[k]个字符和主串中第j个字符比较。若next[k] = k'时,且 pj  = pk'       ,则说明在主串中第j+1个字符之前存在一个长度为k'(即next[k])的最长子串,和模式串中从首字符起长度长度为k'的子串相等,即

         "p1p2p3...p k’"  = "p j-k’+1 p j-k‘+2...p j"  (1 < k' < k < j)            (5)

这就是说 next[j+1] = k' +1 ,即

                       next[j+1] = next[k] +1;

同样的,若pj  != pk'     则将模式继续向右滑动直至将模式中第next[k']个字符串和pj    对齐......,依次类推,直至pj 和模式中某个字符串匹配成功或者不存在k'(1 < k' < j) 满足等式 (5),则

             next[j +1] = 0;

根据以上思路,next函数代码如下

 

 static void get_next(string T, int[] next)
        {
            int i = 0;
            next[0] = -1;
            int j = -1;
            while(i < T.Length-1 )
            {
                if (j == -1 || T[i] == T[j])
                {
                    ++i;
                    ++j;
                    next[i] = j;
                }
                else
                {
                    j = next[j];
                }
            }
        }

 

next函数的算法复杂度在于模式串的长度,模式串的长度m比主串的长度能n要小的多,因此,对整个匹配算法来说,所增加的这点时间是值得的。next函数针对有些情况尚有缺陷,比如模式串aaaab,则回溯时要遍历4个a, 对此情况可直接跳到首位遍历。可适当修改函数如下:

 static void get_next(string T, int[] next)
        {
            int i = 0;
            next[0] = -1;
            int j = -1;
            while(i < T.Length-1 )
            {
                if (j == -1 || T[i] == T[j])
                {
                    ++i;
                    ++j;
                    if (T[i] != T[j])
                        next[i] = j;
                    else

                        next[i] = next[j];
                    //next[i] = j;
                }
                else
                {
                    j = next[j];
                }
            }
        }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值