【笔记】字符串匹配算法

在进行搜索的时候,经常要使用到字符串匹配算法,下面总结几种字符串匹配的算法,以C#代码为例

1、BF匹配算法

  最简单的匹配算法,时间复杂度为O(m*n),原理:逐个匹配,若发现不匹配,则后移一位继续匹配,

  从pos位置开始,在 source 中找出与 target 匹配的子串的位置,若未找到,返回-1

        //BF匹配算法,时间复杂度O(m*n)
        private int BFIndex(string source, string target, int pos)
        {
            int i = pos, j = 0;
            while (i + j < source.Length && j < target.Length)
            {
                if (source[i + j] == target[j])
                {
                    j++;
                }
                else
                {
                    i++;
                    j = 0;
                }
            }
            if (j == target.Length)
            {
                return i;
            }
            else
            {
                return -1;
            }
        }

2、KMP算法

  KMP算法在BF算法的基础上做了改进,减少回溯,时间复杂度为O(m+n)

  首先计算出所有target所有字符的新起点,以减少字符串在不匹配的情况下回退的字符

下面看代码,如果不能理解的话,把代码抄下来,多分析几遍,就会懂了

        private int[] GetNext(string target)
        {
            int k = -1;
            int i = 0;
            
            int[] next = new int[target.Length];
            next[0] = -1;
            while (i < target.Length - 1)
            {
                if (k == -1 || target[k] == target[i])
                {
                    k = k + 1;
                    i = i + 1;
                    next[i] = k;
                }
                else
                {
                    k = next[k];
                }
            }
            return next;
        }
        
        //KMP匹配算法,时间复杂度为O(m+n)
        private int KMPIndex(string source, string target, int pos)
        {
            int[] next = GetNext(target);
            int i = pos, j = 0;
            while (i < source.Length && j < target.Length)
            {
                if (j == -1 || source[i] == target[j])
                {
                    i = i + 1;
                    j = j + 1;
                }
                else
                {
                    j = next[j];
                }
            }
            if (j >= target.Length)
            {
                return (i - target.Length);
            }
            else
            {
                return (-1);
            }  
        }

 3、Horspool匹配算法

  Horspool算法是后缀匹配算法,从最后一个字符向左匹配,然后进行安全移动

 下面是代码

        //Horspool匹配算法,平均时间复杂度O(n)
        private int HorspoolIndex(string source, string target, int pos)
        {
            int sLen = source.Length;
            int tLen = target.Length;
            int Ti = tLen - 1;      //目标字符串匹配位置
            int Si = pos + Ti;      //源字符串匹配位置

            if ((sLen - pos) < tLen)
                return -1;

            while ((Ti > -1) && (Si < sLen))
            {
                if (source[Si] == target[Ti])
                {
                    Ti--;
                    Si--;
                }
                else
                {
                    while ((Ti > -1) && (source[Si] != target[Ti]))
                    {
                        Ti--;
                    }
                    Si += tLen - 1 - Ti;
                    Ti = tLen - 1;
                }
            }

            if (Si < sLen) 
                return(Si + 1);
            else
                return -1;
        }

 4、BM匹配算法

  BM算法在Horspool算法的基础上结合了KMP算法,从而算出最大右移位置,基本思路与Horspool算法相似

  思路:

    从右往左匹配

    匹配失败后,计算出最大右移位置,然后向右移动,继续匹配

  两个规则

    坏字符算法:思想与Horspool差不多,看的懂Horspool的一般不难理解

    好后缀算法:计算目标字符串是否含有已匹配的后缀suffix,如果没有,计算出好后缀(目标字符串中的前缀和后缀一样),然后右移对齐字符串

  废话不多说,下面图解

 代码如下:

        //BM匹配算法,最好情况下的时间复杂度为O(n/m),最坏情况下时间复杂度为O(m·n)
        private int BMIndex(string source, string target, int pos)
        {
            int i = target.Length - 1;
            int j = i;

            while ((j > -1) && i < source.Length)
            {
                if (source[i] == target[j])
                {
                    i--; j--;
                }
                else
                {
                    int moveStep = Math.Max(BadCharacter(target, j, source[i]), GoodSuffix(target, j));
                    i += moveStep;
                }
            }
            if (j == -1)
            {
                return i + 1;
            }
            return -1;
        }
        //返回目标字符串右移位数
        private int BadCharacter(string target, int pos, char ch)
        {
            int j = pos - 1;
            while (j > -1)
            {
                if (target[j] == ch)
                {
                    return pos - j;
                }
                j--;
            }

            return pos;
        }
        //返回目标字符串右移位数
        private int GoodSuffix(string target, int pos)
        {
            string suffix = target.Substring(pos + 1, target.Length - pos - 1);
            //找子串
            int a = target.Substring(0, target.Length - 1).LastIndexOf(suffix);
            if (a > -1)
            {
                return pos - a;
            }
            else
            {
                //找后缀
                return FindSuffix(target, pos);
            }
        }
        //找出前缀与后缀相同的字符,计算出移动位数
        private int FindSuffix(string target, int pos)
        {
            int i = 0;
            int ppos = pos + 1;
            int j = ppos;

            while (j < target.Length)
            {
                if (target[i] == target[j])
                {
                    i++;
                    j++;
                }
                else
                {
                    i = 0;
                    j = ppos = ppos + 1;
                }
            }
            if (i == 0)
            {
                return -1;
            }
            else
            {
                return ppos;
            }
        }

 5、Sunday匹配算法  

        //Sunday匹配算法
        private int SundayIndex(string source, string target)
        {
            int i, j, p;
            i = j = p = 0;

            while (i <= source.Length - target.Length && j < target.Length)
            {
                if (source[p] == target[j])
                {
                    p++;
                    j++;
                }
                else
                {
                    int k = target.Length;
                    while (k > 0)
                    {
                        k--;
                        if (target[k] == source[i + target.Length])
                            break;
                    }
                    i += target.Length - k;
                    p = i;
                    j = 0;
                }
            }
            if (j == target.Length)
            {
                return i;
            }
            return -1;
        }

    

..

原文地址:https://www.cnblogs.com/bomo/p/2796865.html