github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/kmp/kmp.go (about) 1 package kmp 2 3 /* 4 5 kmp算法步骤 6 1. 生成前缀表 (此处kmp算法是根据阮一峰讲解的kmp算法实现的,可能会与其他kmp算法的求前缀表有所区别) 7 阮一峰的方式 是指针指向主串,主串指针可以移动一大段,每次比较都是从子串第一个开始 与 主串指针的位置进行比较 8 其他人讲解的 是指针指向模式串, 通过变动模式串的指针,每次从模式串指针的位置开始(不必每次从字串第一个开始),与 主串指针(每次for都只+1)进行比较 9 2. 统计已匹配字符数 10 3. 查表,根据公式往下一点:移动位数 = 已匹配的字符数 - 对应的部分匹配值(查表获得) 11 12 13 时间复杂度 O(m+n) m是主串的长度, n是匹配串的长度 14 */ 15 // 匹配主中有多少个子串 16 func MarchSubstr(mainStr, pattern []rune) int { 17 if len(mainStr) < len(pattern) { // 子串大于主肯定没有匹配的 18 return 0 19 } 20 preTab := prefixTable2(pattern) //前缀表 21 var ( 22 mp = 0 // 主串的下标 23 mLen = len(mainStr) 24 sLen = len(pattern) 25 marchCnt = 0 // 已匹配数量 26 ) 27 for mp+sLen <= mLen { 28 mCnt := 0 //已匹配的字符个数 29 for sp := 0; sp < sLen; sp++ { 30 if pattern[sp] != mainStr[mp+sp] { 31 break 32 } 33 mCnt++ 34 } 35 if mCnt == sLen { //说明匹配成功 36 //fmt.Printf("匹配成功位置: %d, str:%s \n", mp, string(mainStr[mp:mp+sLen])) 37 marchCnt++ 38 mp++ 39 } else if mCnt == 0 { // 没有匹配往后移一格 40 mp++ 41 } else { 42 // 移动位数 = 已匹配的字符数 - 对应的部分匹配值 43 mp += mCnt - preTab[mCnt-1] 44 } 45 } 46 return marchCnt 47 } 48 49 // 生成前缀表 PrefixTable 50 func prefixTable(s []rune) []int { 51 sLen := len(s) 52 table := make([]int, sLen) 53 table[0] = 0 54 for i := 1; i < sLen; i++ { 55 // 表示 [0,i]的子串 中最长公共前后缀的的长度 56 table[i] = findMaxCommonPreSufNum(s[:i+1]) 57 //fmt.Printf("%s=%d\n", string(s[:i+1]), table[i]) 58 } 59 return table 60 } 61 62 func prefixTable2(s []rune) []int { 63 m := len(s) 64 nexts := make([]int, m) 65 for index := range nexts { 66 nexts[index] = -1 67 } 68 69 for i := 1; i < m-1; i++ { 70 j := nexts[i-1] // 上一次匹配前缀与后缀匹配最大长度,前缀末尾位置 71 // j+1 前缀的末尾位置的后面一位 比如 ababacd 模式串, 这时 i=4时s[i]='a',j=1, j+1的位置就是第2个a 72 for s[j+1] != s[i] && j >= 0 { 73 // 如果 目s[j+1] 表示最大前缀后一位字符 不等于 当前字符,就进去查找次匹配长度的位置(相当于查找了已经求解过的子问) 74 // 看次长度的后面一位 是否相等 当前字符 75 j = nexts[j] 76 } 77 78 if s[j+1] == s[i] { 79 j += 1 80 } 81 82 nexts[i] = j 83 } 84 // 将下标 改为长度 85 table := make([]int, m) 86 for i := range nexts { 87 table[i] = 1 + nexts[i] 88 } 89 //fmt.Println(table) 90 return table 91 } 92 93 // 查找前缀与后缀的共有最大长度 94 func findMaxCommonPreSufNum(s []rune) int { 95 // 此处的思路,每次从最长的进行比较, 96 // 可以通过动态规划, 从最短的开始比较进行递推 97 length := len(s) 98 maxLen := length - 1 99 for maxLen > 0 { 100 if eqRuneSlice(s[:maxLen], s[length-maxLen:]) { 101 return maxLen 102 } 103 maxLen-- 104 } 105 return 0 106 } 107 108 // 比较rune切片元素相等情况 109 func eqRuneSlice(a, b []rune) bool { 110 if len(a) != len(b) { 111 return false 112 } 113 for i := 0; i < len(a); i++ { 114 if a[i] != b[i] { 115 return false 116 } 117 } 118 return true 119 }