KMP算法(Knuth Morris Pratt)

  1. pub fn knuth_morris_pratt(st: String, pat: String) -> Vec<usize> {
  2. if st.is_empty() || pat.is_empty() {
  3. return vec![];
  4. }
  5. let string = st.into_bytes();
  6. let pattern = pat.into_bytes();
  7. // build the partial match table
  8. let mut partial = vec![0];
  9. for i in 1..pattern.len() {
  10. let mut j = partial[i - 1];
  11. while j > 0 && pattern[j] != pattern[i] {
  12. j = partial[j - 1];
  13. }
  14. partial.push(if pattern[j] == pattern[i] { j + 1 } else { j });
  15. }
  16. // and read 'string' to find 'pattern'
  17. let mut ret = vec![];
  18. let mut j = 0;
  19. for (i, &c) in string.iter().enumerate() {
  20. while j > 0 && c != pattern[j] {
  21. j = partial[j - 1];
  22. }
  23. if c == pattern[j] {
  24. j += 1;
  25. }
  26. if j == pattern.len() {
  27. ret.push(i + 1 - j);
  28. j = partial[j - 1];
  29. }
  30. }
  31. ret
  32. }
  33. #[cfg(test)]
  34. mod tests {
  35. use super::*;
  36. #[test]
  37. fn each_letter_matches() {
  38. let index = knuth_morris_pratt("aaa".to_string(), "a".to_string());
  39. assert_eq!(index, vec![0, 1, 2]);
  40. }
  41. #[test]
  42. fn a_few_separate_matches() {
  43. let index = knuth_morris_pratt("abababa".to_string(), "ab".to_string());
  44. assert_eq!(index, vec![0, 2, 4]);
  45. }
  46. #[test]
  47. fn one_match() {
  48. let index =
  49. knuth_morris_pratt("ABC ABCDAB ABCDABCDABDE".to_string(), "ABCDABD".to_string());
  50. assert_eq!(index, vec![15]);
  51. }
  52. #[test]
  53. fn lots_of_matches() {
  54. let index = knuth_morris_pratt("aaabaabaaaaa".to_string(), "aa".to_string());
  55. assert_eq!(index, vec![0, 1, 4, 7, 8, 9, 10]);
  56. }
  57. #[test]
  58. fn lots_of_intricate_matches() {
  59. let index = knuth_morris_pratt("ababababa".to_string(), "aba".to_string());
  60. assert_eq!(index, vec![0, 2, 4, 6]);
  61. }
  62. #[test]
  63. fn not_found0() {
  64. let index = knuth_morris_pratt("abcde".to_string(), "f".to_string());
  65. assert_eq!(index, vec![]);
  66. }
  67. #[test]
  68. fn not_found1() {
  69. let index = knuth_morris_pratt("abcde".to_string(), "ac".to_string());
  70. assert_eq!(index, vec![]);
  71. }
  72. #[test]
  73. fn not_found2() {
  74. let index = knuth_morris_pratt("ababab".to_string(), "bababa".to_string());
  75. assert_eq!(index, vec![]);
  76. }
  77. #[test]
  78. fn empty_string() {
  79. let index = knuth_morris_pratt("".to_string(), "abcdef".to_string());
  80. assert_eq!(index, vec![]);
  81. }
  82. }