前缀与后缀的作用其实就是找到相同的元素长度,在进行比较的字符串的时候,可以直接进行相同元素的比较,而不是一步一步返回来比较,因此提高了匹配效率。
(1)首先,字符串”BBC ABCDAB ABCDABCDABDE”的第一个字符与搜索词”ABCDABD”的第一个字符比较,不匹配,移到下一个字符 (2)不匹配,以一个字符比较 (3)移到A时候,这时候匹配了 (4)此时直到D所对应的不匹配了,这时候该怎么办呢, (5)这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把”搜索位置”移到已经比较过的位置,重比一遍。 (6)但是你感觉这样好吗?肯定是不好的,那该怎么办呢,这时候前缀和后缀就派上用场了,我们不需要直接从头比较,我们只要直接比较相同元素就可以了,因为这时候又重新达到了一个匹配。
“部分匹配值”就是”前缀”和”后缀”的最长的共有元素的长度。以”ABCDABD”为例, - “A”的前缀和后缀都为空集,共有元素的长度为0; - “AB”的前缀为[A],后缀为[B],共有元素的长度为0; - “ABC”的前缀为[A, AB],后缀为[BC, C],共有元素的长度0; - “ABCD”的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0; - “ABCDA”的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为”A”,长度为1; - “ABCDAB”的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为”AB”,长度为2; - “ABCDABD”的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。
(1)求next
void makeNext(const char P[],int next[]) { int q,k;//q:模版字符串下标;k最大前后缀长度 int m = strlen(P); next[0] = 0; for (q = 1,k = 0; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对 应的next值 { while(k > 0 && P[q] != P[k]) k = next[k-1]; if (P[q] == P[k]) { k++; } next[q] = k; } }(2)求解匹配
int kmp(const char T[],const char P[],int next[]) { int n,m; int i,q; n = strlen(T); m = strlen(P); makeNext(P,next); for (i = 0,q = 0; i < n; ++i) { while(q > 0 && P[q] != T[i]) q = next[q-1]; if (P[q] == T[i]) { q++; } if (q == m) { printf("Pattern occurs with shift:%d\n",(i-m+1)); } } }