• KMP算法


     1            #region KMP算法
     2         /// <summary>
     3         /// KMP算法
     4         /// 求一个字符串的回溯函数。
     5         /// 约定序列下标从0开始。
     6         /// 回溯函数是整数集[0,n-1]到N的映射,n为字符串的长度。
     7         /// 回溯函数的定义:
     8         /// 设存在非空序列L,i为其合法下标;
     9         /// L[i]的前置序列集为:{空集,L中所有以i-1为最后一个元素下标的子序列}
    10         /// L的前置序列集为:{空集,L中所有以0为第一个元素下标的子序列}
    11         /// 下标i的回溯函数值的定义为:
    12         /// 如果i=0,回溯函数值为-1
    13         /// 否则i的回溯函数值为i的前置序列集和L的前置序列集中相等元素的最大长度,但是相等的两个元素不能是L中的同一个子串,例如[0-i,1]~[i-1,0]reversed
    14         /// 换句话说是,设集合V={x,x属于i的前置序列集,并且x属于L的前置序列集,并且x的长度小于i},回溯函数值为max(V).length
    15         /// 当i=0时并不存在这样的一个x,所以约定此时的回溯函数值为-1
    16         /// 回溯函数的意义:
    17         /// 如果子串中标号为j的字符同主串失配,那么将子串回溯到next[j]继续与主串匹配,如果next[j]=-1,则主串的匹配点后移一位,同子串的第一个元素开始匹配。
    18         /// 同一般的模式匹配算法相比,kmp通过回溯函数在失配的情况下跳过了若干轮匹配(向右滑动距离可能大于1)
    19         /// kmp算法保证跳过去的这些轮匹配一定是失配的,这一点可以证明
    20         /// </summary>
    21         /// <param name="pattern">模式串,上面的注释里将其称为子串</param>
    22         /// <returns>回溯函数是kmp算法的核心,本函数依照其定义求出回溯函数,KMP函数依照其意义使用回溯函数。</returns>
    23         public static int[] Next(string pattern)
    24         {
    25             int[] next = new int[pattern.Length];
    26             next[0] = -1;
    27             if (pattern.Length < 2) //如果只有1个元素不用kmp效率会好一些
    28             {
    29                 return next;
    30             }
    31             next[1] = 0;    //第二个元素的回溯函数值必然是0,可以证明:
    32             //1的前置序列集为{空集,L[0]},L[0]的长度不小于1,所以淘汰,空集的长度为0,故回溯函数值为0
    33             int i = 2;  //正被计算next值的字符的索引
    34             int j = 0;  //计算next值所需要的中间变量,每一轮迭代初始时j总为next[i-1]
    35             while (i < pattern.Length)    //很明显当i==pattern.Length时所有字符的next值都已计算完毕,任务已经完成
    36             { //状态点
    37                 if (pattern[i - 1] == pattern[j])   //首先必须记住在本函数实现中,迭代计算next值是从第三个元素开始的
    38                 {   //如果L[i-1]等于L[j],那么next[i] = j + 1
    39                     next[i++] = ++j;
    40                 }
    41                 else
    42                 {   //如果不相等则检查next[i]的下一个可能值----next[j]
    43                     j = next[j];
    44                     if (j == -1)    //如果j == -1则表示next[i]的值是1
    45                     {   //可以把这一部分提取出来与外层判断合并
    46                         //书上的kmp代码很难理解的一个原因就是已经被优化,从而遮蔽了其实际逻辑
    47                         next[i++] = ++j;
    48                     }
    49                 }
    50             }
    51             return next;
    52         }
    53 
    54         /// <summary>
    55         /// KMP函数同普通的模式匹配函数的差别在于使用了next函数来使模式串一次向右滑动多位成为可能
    56         /// next函数的本质是提取重复的计算
    57         /// </summary>
    58         /// <param name="source">主串</param>
    59         /// <param name="pattern">用于查找主串中一个位置的模式串</param>
    60         /// <returns>-1表示没有匹配,否则返回匹配的标号</returns>
    61         public static int ExecuteKMP(string source, string pattern)
    62         {
    63             int[] next = Next(pattern);
    64             int i = 0;  //主串指针
    65             int j = 0;  //模式串指针
    66             //如果子串没有匹配完毕并且主串没有搜索完成
    67             while (j < pattern.Length && i < source.Length)
    68             {
    69                 if (source[i] == pattern[j])    //i和j的逻辑意义体现于此,用于指示本轮迭代中要判断是否相等的主串字符和模式串字符
    70                 {
    71                     i++;
    72                     j++;
    73                 }
    74                 else
    75                 {
    76                     j = next[j];    //依照指示迭代回溯
    77                     if (j == -1)    //回溯有情况,这是第二种
    78                     {
    79                         i++;
    80                         j++;
    81                     }
    82                 }
    83             }
    84             //如果j==pattern.Length则表示循环的退出是由于子串已经匹配完毕而不是主串用尽
    85             return j < pattern.Length ? -1 : i - j;
    86         }
    87         #endregion
    工欲善其事,必先利其器。
  • 相关阅读:
    linux下mysql区分大小写的内容
    jar包 pom
    项目的考虑
    webservice
    MySQL外键设置中的的 Cascade、NO ACTION、Restrict、SET NULL
    JVM参数最佳实践:元空间的初始大小和最大大小
    JVM问题排查工具:Serviceability-Agent介绍
    Spring Boot 2.x基础教程:构建RESTful API与单元测试
    彻底搞懂JVM类加载器:基本概念
    如何解决90%的问题?10位阿里大牛公布方法
  • 原文地址:https://www.cnblogs.com/zhangzhu/p/2836152.html
Copyright © 2020-2023  润新知