串的模式匹配算法,主要对子串可进行固定位运算。常见的模式匹配算法包括BF算法和KMP算法,其中BF算法是最直观简单的,但效率较低。KMP算法通过之前匹配的结果进行合理的选择下一步指针指向的位置。

1. BF算法 

  假设存在2个字符串S和T,其中S为主串,T为模式串,字符串对应的下标分别用i和j表示,判断其是否匹配。S=“abababcabcacbab”,T=”abcac”。

  BF模式匹配中将主串和子串的第一个元素进行对比,如果相等,i和j各向后移动一位,即i=i+1,j=j+1。如果不相同,则i向后移动一位,模式串T从第一个字符开始进行对比判断是否相同。

  具体算法过程如下图所示:

BF算法的c#实现如下所示: 

static void Main(string[] args)
        {
            Char[] s = {\'a\',\'b\',\'a\',\'b\',\'c\',\'a\',\'b\',\'c\',\'a\',\'c\',\'b\',\'a\',\'b\'};
            Char[] t = { \'a\', \'b\', \'c\', \'a\', \'c\' };
            int result=BruteForce(s,t);
            Console.WriteLine("子串在主串中中匹配的起始位置下标为:{0}", result);
            Console.Read();

        }
        /// <summary>
        /// 模式匹配算法——BF算法
        /// 时间复杂度为:最好的情况O(n+m);最坏的情况:O(n*m)
        /// </summary>
        /// <param name="s"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public static int BruteForce(Char[] s,Char[] t)
        {
            if (s.Length == 0 || t.Length == 0)
                Console.WriteLine("请重新输入字符串");
           int i=0;
           int j=0;
           int num = 0;
            if(i < s.Length)
            {
                while (j < t.Length)
                {
                    if (s[i] == t[j])
                    {
                        i++;
                        j++;
                    }
                    else
                    {
                        num++;
                        i = num;
                        j = 0;
                    }
                }
             }
            return i-t.Length;
        } 

 2. KMP算法

  作者:海纳
  链接:https://www.zhihu.com/question/21923021/answer/281346746
  来源:知乎

  KMP算法的核心,是一个被称为部分匹配表(Partial Match Table)的数组。对于字符串“abababca”,它的PMT如下表所示:

char a b a b a b c a
index 0 1 2 3 4 5 6 7
value 0 0 1 2 3 4 0 1

  就像例子中所示的,如果待匹配的模式字符串有8个字符,那么PMT就会有8个值。

  我先解释一下字符串的前缀和后缀。如果字符串A和B,存在A=BS,其中S是任意的非空字符串,那就称B为A的前缀。例如,”Harry”的前缀包括{”H”, ”Ha”, ”Har”, ”Harr”},我们把所有前缀组成的集合,称为字符串的前缀集合。同样可以定义后缀A=SB, 其中S是任意的非空字符串,那就称B为A的后缀,例如,”Potter”的后缀包括{”otter”, ”tter”, ”ter”, ”er”, ”r”},然后把所有后缀组成的集合,称为字符串的后缀集合。要注意的是,字符串本身并不是自己的后缀。

  有了这个定义,就可以说明PMT中的值的意义了。PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。

  以上述字符串为例说明计算其value值。

字符串 前缀 后缀 value
a null null 0
ab a b 0
aba {a,ab} {ba,a} 1
abab {a,ab,aba} {bab,ab,b} 2
ababa {a,ab,aba,abab} {baba,aba,ba,a} 3
ababab {a,ab,aba,abab,ababa} {babab,abab,bab,ab,b} 4
abababc {a,ab,aba,abab,ababa,ababab} {bababc,ababc,babc,abc,bc,c} 0
abababca {a,ab,aba,abab,ababa,ababab,abababc} {bababca,ababca,babca,abca,bca,ca,a} 1

  解释清楚这个表之后,我们再来看如何使用这个表来加速字符串的查找及其原理。

  如下图所示,字符串进行匹配时,如果在第J位不匹配,那么主串中S[i-j]~~S[i]位的元素与模式串中T[0]~~T[j-1]位相同。此时,下一步进行指针移动时,保持i指针不动,模式串T的指针后移至PMT[j-1]。

  简言之,以图中的例子来说,在 i 处失配,那么主字符串和模式字符串的前边6位就是相同的。又因为模式字符串的前6位,它的前4位前缀和后4位后缀是相同的,所以我们推知主字符串i之前的4位和模式字符串开头的4位是相同的。就是图中的灰色部分。那这部分就不用再比较了。(j移动的位数即next值)

  有了上面的思路,我们就可以使用PMT加速字符串的查找了。我们看到如果是在 j 位 失配,那么影响 j 指针回溯的位置的其实是第 j −1 位的 PMT 值,所以为了编程的方便, 我们不直接使用PMT数组,而是将PMT数组向后偏移一位。我们把新得到的这个数组称为next数组。下面给出根据next数组进行字符串匹配加速的字符串匹配程序。其中要注意的一个技巧是,在把PMT进行向右偏移时,第0位的值,我们将其设成了-1,这只是为了编程的方便,并没有其他的意义。在本节的例子中,next数组如下表所示。

char a b a b a b c a
index 0 1 2 3 4 5 6 7
pmt 0 0 1 2 3 4 0 1
next -1 0 0 1 2 3 4 0

 求解next数组的具体过程如下:

j=1时,next[j]=0;

 

posted on
2019-01-11 15:39 
竹影横扫窗 
阅读(365
评论(0
编辑 
收藏 
举报

版权声明:本文为mo-lu原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/mo-lu/articles/10252344.html