今天看到了一篇关于KMP算法的讲解的文章,很难得,讲得非常清楚。分享给大家,希望对大家有帮助。http://kb.cnblogs.com/page/176818/
我自己基于这个讲解的内容作了一个实现,效果还不错,码代码的功力有限,还请大家多指正其中可以改进的地方。
1 using System.Collections.Generic; 2 3 namespace KMPImplementation 4 { 5 /// <summary> 6 /// 该类用于生成KMP算法中,需要用的部分匹配表 7 /// </summary> 8 public class PartialMatchTable 9 { 10 private string data; 11 List<int> OffsetArray = new List<int>(); 12 13 /// <summary> 14 /// 在构造函数中获得匹配模式,并计算产生部分匹配表 15 /// </summary> 16 /// <param name="para"></param> 17 public PartialMatchTable(string para) 18 { 19 data = para; 20 GenerateOffsetArray(); 21 } 22 23 /// <summary> 24 /// 计算一个字符串中,最长的相同前缀与后缀的长度 25 /// </summary> 26 /// <param name="str">需要计算的字符串</param> 27 /// <returns>长度值</returns> 28 private int GetMaxPrefixPostfixLength(string str) 29 { 30 int cnt = 0; 31 if (str != null) 32 { 33 int len = str.Length - 1; 34 for (int i = 1; i <= len; i++) 35 { 36 string pre = str.Substring(0, i); 37 string post = str.Substring(str.Length - i, i); 38 if (pre == post) 39 cnt = cnt < i ? i : cnt; 40 } 41 } 42 return cnt; 43 } 44 45 /// <summary> 46 /// 计算给定字符串各个字符对应的偏移值 47 /// </summary> 48 private void GenerateOffsetArray() 49 { 50 if (string.IsNullOrEmpty(data)) 51 return; 52 if (data.Length == 1) 53 return; 54 for (int i = 0; i < data.Length; i++) 55 { 56 string sub = data.Substring(0, i + 1); 57 int max = GetMaxPrefixPostfixLength(sub); 58 OffsetArray.Add(max); 59 } 60 } 61 62 /// <summary> 63 /// 从部分匹配表中,取出相应的值 64 /// </summary> 65 /// <param name="i"></param> 66 /// <returns></returns> 67 public int GetOffset(int i) 68 { 69 int val = 0; 70 if (OffsetArray.Count > i) 71 val = OffsetArray[i]; 72 return val; 73 } 74 75 /// <summary> 76 /// 调试用的,打印计算结果 77 /// </summary> 78 public void PrintDic() 79 { 80 int cnt = data.Length; 81 for (int i = 0; i < cnt; i++) 82 { 83 System.Diagnostics.Debug.WriteLine("{0},{1} ", data[i], OffsetArray[i]); 84 } 85 } 86 } 87 }
1 using System.Collections.Generic; 2 3 namespace KMPImplementation 4 { 5 /// <summary> 6 /// 该类实现了KMP匹配算法,以此在目标串中查找匹配模式的index 7 /// </summary> 8 public class KMPImplementation 9 { 10 private string pattern; // 记录模式 11 private string data;// 记录目标串 12 PartialMatchTable si;// 记录部分匹配表 13 14 /// <summary> 15 /// 在构造函数中,将异常的输入,如null,过滤处理 16 /// </summary> 17 /// <param name="dt">需要匹配的目标串</param> 18 /// <param name="ptn">匹配模式</param> 19 public KMPImplementation(ref string dt, string ptn) 20 { 21 si = new PartialMatchTable(ptn); 22 if (dt == null || ptn == null) 23 { 24 data = string.Empty; 25 pattern = string.Empty; 26 } 27 else 28 { 29 data = dt; 30 pattern = ptn; 31 } 32 } 33 34 /// <summary> 35 /// 查找目标串中的第一个匹配项的index 36 /// </summary> 37 /// <returns></returns> 38 public int FindFirstIndex() 39 { 40 int idx = -1; 41 int datalen = data.Length; 42 int patternlen = pattern.Length; 43 // 空串查找空串的情况 44 if (datalen == patternlen && patternlen == 0) 45 return 0; 46 // 对应目标串比模式短的情况,直接返回找不到,即是-1 47 if (datalen >= patternlen) 48 { 49 datalen = datalen - patternlen; 50 for (int i = 0; i <= datalen; ) 51 { 52 for (int j = 0; j < patternlen; ) 53 { 54 if (data[i + j] == pattern[j])// 模式与目标串的字符相同,继续比较下一个 55 { 56 j++; 57 if (j == patternlen)// 当模式的index指到了最后一个位置,表明查找到了 58 return i; 59 } 60 else// 当模式与目标串不匹配时 61 { 62 if (j == 0)// 如果第一个字符都不匹配,继续比较下一个位置 63 i++; 64 else 65 i += j - si.GetOffset(j - 1);// 如果已经发生了部分匹配,查找部分匹配表来确定,当前应该将目标串的index向后移动的量 66 break; 67 } 68 } 69 } 70 } 71 return idx; 72 } 73 74 /// <summary> 75 /// 查找目标串中的所有匹配项的index,以List形式返回 76 /// </summary> 77 /// <returns></returns> 78 public List<int> FindAllIndex() 79 { 80 List<int> list = new List<int>(); 81 int datalen = data.Length; 82 int patternlen = pattern.Length; 83 // 空串查找空串的情况 84 if (datalen == patternlen && patternlen == 0) 85 { 86 list.Add(0); 87 return list; 88 } 89 // 对应目标串比模式短的情况,直接返回找不到,即是-1 90 if (datalen >= patternlen) 91 { 92 datalen = datalen - patternlen; 93 for (int i = 0; i <= datalen; ) 94 { 95 for (int j = 0; j < patternlen; ) 96 { 97 if (data[i + j] == pattern[j])// 模式与目标串的字符相同,继续比较下一个 98 { 99 j++; 100 if (j == patternlen)// 当模式的index指到了最后一个位置,表明查找到了 101 { 102 list.Add(i); 103 i++; 104 } 105 } 106 else// 当模式与目标串不匹配时 107 { 108 if (j == 0)// 如果第一个字符都不匹配,继续比较下一个位置 109 i++; 110 else 111 i += j - si.GetOffset(j - 1);// 如果已经发生了部分匹配,查找部分匹配表来确定,当前应该将目标串的index向后移动的量 112 break; 113 } 114 } 115 } 116 } 117 if (list.Count == 0) 118 list.Add(-1); 119 return list; 120 } 121 } 122 }
这里实现了两个方法,一个是查找第一个匹配项,一个是查找所有的匹配项,方便调用;但是,显而易见的是,两个方法有太多的相同的地方,可以再做一次抽象。懒病犯了,就没接着改进了。