[每天默写一个算法]KMP
[每天默写一个算法]KMP
作业要求:默写String的KMP算法。
KMP是经典的字符串匹配算法。复杂度为O(n+m)
1 public static class StringKMP 2 { 3 /// <summary> 4 /// This indicates that no pattern found from source. 5 /// </summary> 6 public const int KMPNoMatch = -1; 7 /// <summary> 8 /// Special value of next[] array, which means i should be increased by 1 and j sould be reset to 0. 9 /// </summary> 10 public const int FirstBlood = -1; 11 /// <summary> 12 /// Find first match for specified pattern in the source. 13 /// </summary> 14 /// <param name="source"></param> 15 /// <param name="pattern"></param> 16 /// <returns></returns> 17 public static int KMP(this String source, String pattern) 18 { 19 if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(pattern)) 20 { return KMPNoMatch; } 21 var i = 0; var j = 0; var result = KMPNoMatch; 22 var nextVal = GetNextVal(pattern); 23 24 while (i < source.Length && j < pattern.Length) 25 { 26 if (j == FirstBlood) 27 {// source[i] does NOT equal with pattern[0], so i should be increased by 1 and j should be reset to 0. 28 i++; j = 0; 29 } 30 else if (source[i].Equals(pattern[j])) 31 { 32 i++; j++; 33 } 34 else 35 {// Get next j that should be compared with. 36 j = nextVal[j]; 37 } 38 } 39 40 if (j >= pattern.Length)// Match succeeded. 41 { result = i - pattern.Length; } 42 43 return result; 44 } 45 /// <summary> 46 /// nextVal[j]: source[i] should compare with pattern[ nextVal[j] ] in next loop 47 /// <para>if source[i] does NOT equal with pattern[j].</para> 48 /// <para>Specially, if source[i] does NOT equal with pattern[0], then i should be increased by 1</para> 49 /// <para>and j should be reset to 0.</para> 50 /// <para>So we should always set nextVal[0] = FirstBlood.</para> 51 /// </summary> 52 /// <param name="pattern"></param> 53 /// <returns></returns> 54 private static int[] GetNextVal(String pattern) 55 { 56 var j = 0; var k = -1; 57 var nextVal = new int[pattern.Length]; 58 59 nextVal[0] = FirstBlood; 60 61 while (j < pattern.Length - 1) 62 { 63 if ((k == -1) || (pattern[j].Equals(pattern[k]))) 64 { 65 j++; k++; 66 if (!(pattern[j].Equals(pattern[k]))) 67 { nextVal[j] = k; } 68 else 69 { nextVal[j] = nextVal[k]; } 70 } 71 else 72 { k = nextVal[k]; } 73 } 74 75 return nextVal; 76 } 77 }
字符串能匹配,其他类型的串(数组Array和泛型数组IList<T>)也就可以匹配。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace System 7 { 8 public static class StringKMP 9 { 10 /// <summary> 11 /// This indicates that no pattern found from source. 12 /// </summary> 13 public const int KMPNoMatch = -1; 14 /// <summary> 15 /// Special value of next[] array, which means i should be increased by 1 and j sould be reset to 0. 16 /// </summary> 17 private const int FirstBlood = -1; 18 /// <summary> 19 /// Find first match for specified pattern in the source. 20 /// </summary> 21 /// <param name="source"></param> 22 /// <param name="pattern"></param> 23 /// <returns></returns> 24 public static int KMP(this String source, String pattern) 25 { 26 if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(pattern)) 27 { return KMPNoMatch; } 28 var i = 0; var j = 0; var result = KMPNoMatch; 29 var nextVal = GetNextVal(pattern); 30 31 while (i < source.Length && j < pattern.Length) 32 { 33 if (j == FirstBlood) 34 {// source[i] does NOT equal with pattern[0], so i should be increased by 1 and j should be reset to 0. 35 i++; j = 0; 36 } 37 else if (source[i].Equals(pattern[j])) 38 { 39 i++; j++; 40 } 41 else 42 {// Get next j that should be compared with. 43 j = nextVal[j]; 44 } 45 } 46 47 if (j >= pattern.Length)// Match succeeded. 48 { result = i - pattern.Length; } 49 50 return result; 51 } 52 /// <summary> 53 /// nextVal[j]: source[i] should compare with pattern[ nextVal[j] ] in next loop 54 /// <para>if source[i] does NOT equal with pattern[j].</para> 55 /// <para>Specially, if source[i] does NOT equal with pattern[0], then i should be increased by 1</para> 56 /// <para>and j should be reset to 0.</para> 57 /// <para>So we should always set nextVal[0] = FirstBlood.</para> 58 /// </summary> 59 /// <param name="pattern"></param> 60 /// <returns></returns> 61 private static int[] GetNextVal(String pattern) 62 { 63 var j = 0; var k = -1; 64 var nextVal = new int[pattern.Length]; 65 66 nextVal[0] = FirstBlood; 67 68 while (j < pattern.Length - 1) 69 { 70 if ((k == -1) || (pattern[j].Equals(pattern[k]))) 71 { 72 j++; k++; 73 if (!(pattern[j].Equals(pattern[k]))) 74 { nextVal[j] = k; } 75 else 76 { nextVal[j] = nextVal[k]; } 77 } 78 else 79 { k = nextVal[k]; } 80 } 81 82 return nextVal; 83 } 84 } 85 86 public static class ArrayKMP 87 { 88 /// <summary> 89 /// This indicates that no pattern found from source. 90 /// </summary> 91 public const int KMPNoMatch = -1; 92 /// <summary> 93 /// Special value of next[] array, which means i should be increased by 1 and j sould be reset to 0. 94 /// </summary> 95 private const int FirstBlood = -1; 96 /// <summary> 97 /// Find first match for specified pattern in the source. 98 /// </summary> 99 /// <param name="source"></param> 100 /// <param name="pattern"></param> 101 /// <returns></returns> 102 public static int KMP(this Array source, Array pattern) 103 { 104 if (source == null || pattern == null || source.Length == 0 || pattern.Length == 0) 105 { return KMPNoMatch; } 106 var i = 0; var j = 0; var result = KMPNoMatch; 107 var nextVal = GetNextVal(pattern); 108 109 while (i < source.Length && j < pattern.Length) 110 { 111 if (j == FirstBlood) 112 {// source[i] does NOT equal with pattern[0], so i should be increased by 1 and j should be reset to 0. 113 i++; j = 0; 114 } 115 else if (source.GetValue(i).Equals(pattern.GetValue(j))) 116 { 117 i++; j++; 118 } 119 else 120 {// Get next j that should be compared with. 121 j = nextVal[j]; 122 } 123 } 124 125 if (j >= pattern.Length)// Match succeeded. 126 { result = i - pattern.Length; } 127 128 return result; 129 } 130 /// <summary> 131 /// nextVal[j]: source[i] should compare with pattern[ nextVal[j] ] in next loop 132 /// <para>if source[i] does NOT equal with pattern[j].</para> 133 /// <para>Specially, if source[i] does NOT equal with pattern[0], then i should be increased by 1</para> 134 /// <para>and j should be reset to 0.</para> 135 /// <para>So we should always set nextVal[0] = FirstBlood.</para> 136 /// </summary> 137 /// <param name="pattern"></param> 138 /// <returns></returns> 139 private static int[] GetNextVal(Array pattern) 140 { 141 var j = 0; var k = -1; 142 var nextVal = new int[pattern.Length]; 143 144 nextVal[0] = FirstBlood; 145 146 while (j < pattern.Length - 1) 147 { 148 if ((k == -1) || (pattern.GetValue(j).Equals(pattern.GetValue(k)))) 149 { 150 j++; k++; 151 if (!(pattern.GetValue(j).Equals(pattern.GetValue(k)))) 152 { nextVal[j] = k; } 153 else 154 { nextVal[j] = nextVal[k]; } 155 } 156 else 157 { k = nextVal[k]; } 158 } 159 160 return nextVal; 161 } 162 } 163 164 public static class IListKMP 165 { 166 sealed class DefaultComparer<T> : IComparer<T> 167 { 168 private static readonly DefaultComparer<T> instance = new DefaultComparer<T>(); 169 170 public static DefaultComparer<T> Instance 171 { 172 get { return DefaultComparer<T>.instance; } 173 } 174 175 176 int IComparer<T>.Compare(T x, T y) 177 { 178 if (x.Equals(y)) { return 0; } 179 else { return 1; } 180 } 181 } 182 183 /// <summary> 184 /// This indicates that no pattern found from source. 185 /// </summary> 186 public const int KMPNoMatch = -1; 187 /// <summary> 188 /// Special value of next[] array, which means i should be increased by 1 and j sould be reset to 0. 189 /// </summary> 190 private const int FirstBlood = -1; 191 /// <summary> 192 /// Find first match for specified pattern in the source. 193 /// </summary> 194 /// <param name="source"></param> 195 /// <param name="pattern"></param> 196 /// <param name="comparer"></param> 197 /// <returns></returns> 198 public static int KMP<T>(this IList<T> source, IList<T> pattern, IComparer<T> comparer = null) 199 { 200 if (source == null || pattern == null || source.Count == 0 || pattern.Count == 0) 201 { return KMPNoMatch; } 202 203 if (comparer == null) { comparer = DefaultComparer<T>.Instance; } 204 205 var i = 0; var j = 0; var result = KMPNoMatch; 206 var nextVal = GetNextVal(pattern, comparer); 207 208 while (i < source.Count && j < pattern.Count) 209 { 210 if (j == FirstBlood) 211 {// source[i] does NOT equal with pattern[0], so i should be increased by 1 and j should be reset to 0. 212 i++; j = 0; 213 } 214 else if (comparer.Compare(source[i], pattern[j]) == 0) //(source[i].Equals(pattern[j])) 215 { 216 i++; j++; 217 } 218 else 219 {// Get next j that should be compared with. 220 j = nextVal[j]; 221 } 222 } 223 224 if (j >= pattern.Count)// Match succeeded. 225 { result = i - pattern.Count; } 226 227 return result; 228 } 229 230 /// <summary> 231 /// nextVal[j]: source[i] should compare with pattern[ nextVal[j] ] in next loop 232 /// <para>if source[i] does NOT equal with pattern[j].</para> 233 /// <para>Specially, if source[i] does NOT equal with pattern[0], then i should be increased by 1</para> 234 /// <para>and j should be reset to 0.</para> 235 /// <para>So we should always set nextVal[0] = FirstBlood.</para> 236 /// </summary> 237 /// <param name="pattern"></param> 238 /// <returns></returns> 239 private static int[] GetNextVal<T>(IList<T> pattern, IComparer<T> comparer) 240 { 241 var j = 0; var k = -1; 242 var nextVal = new int[pattern.Count]; 243 244 nextVal[0] = FirstBlood; 245 246 while (j < pattern.Count - 1) 247 { 248 if ((k == -1) || (comparer.Compare(pattern[j], pattern[k]) == 0)) 249 { 250 j++; k++; 251 if (!(comparer.Compare(pattern[j], pattern[k]) == 0)) 252 { nextVal[j] = k; } 253 else 254 { nextVal[j] = nextVal[k]; } 255 } 256 else 257 { k = nextVal[k]; } 258 } 259 260 return nextVal; 261 } 262 263 } 264 265 }
微信扫码,自愿捐赠。天涯同道,共谱新篇。
微信捐赠不显示捐赠者个人信息,如需要,请注明联系方式。 |