• 【笔记】字符串匹配算法


    在进行搜索的时候,经常要使用到字符串匹配算法,下面总结几种字符串匹配的算法,以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;
            }

        

    ..

  • 相关阅读:
    ThinkPHP 5 中AJAX跨域请求头设置方法【转】
    phpstorm激活大全【转】
    PHP严重致命错误处理:php Fatal error: Cannot redeclare class or function【转】
    php的dirname(__FILE__)和dirname(dirname(__FILE__))使用总结【转】
    php 设置临时内存和超时设置脚本最大执行时间
    mysql 实现表连接(左,右,内,全连接)【转】
    Maximum execution time of 30 seconds exceeded解决办法【转】
    常用邮箱SMTP服务器地址大全
    叉积(POJ
    乘法逆元(P3811)(四种方法)
  • 原文地址:https://www.cnblogs.com/bomo/p/2796865.html
Copyright © 2020-2023  润新知