萌新的看毛片(kmp)学习日记(下标从0开始)

来源:互联网 发布:淘宝买家怎么买运费险 编辑:程序博客网 时间:2021/06/19 13:55

bf匹配算法的思想是每次主串和模式串都从0开始匹配,当出现失匹的时候模式串将回退到起点,主串将回退到本次匹配起点的下一位置然后重新进行匹配,这样的匹配方式虽然好理解但是时间复杂度比较高,而kmp每次出现失匹的情况只有模式串回退,主串不回退,如果当前失匹点的位置为s[i],并且前k个元素和从头往后数k个元素匹配(即:"s[0]s[1]s[2]…s[k-1] = "s[i-k].....s[i-1]"),那么主串从上一位置一直往前k个元素和s[0]到s[k--1]肯定是匹配的,所以只需要比较当前位置和模式串的k位置是否匹配就可以了.


/*a为主串,b为模式串,n为主串长度,m为模式串长度,从主串的第pos位置开始往后比较*/int kmp(char a[], char b[], int n, int m, int pos){int i = pos, j = 0;//将主串的初始位置指向pos位置,模式串从头开始比较while(i < n){if(j == -1 || a[i] == b[j])//如果当前位置匹配,那么继续匹配下一位置;{/*j == -1的情况是如果上次在某位置失匹并且模式串中没有一个k位置使得模式串中当前位置的前k个位置和从头开始k个位置相匹配那么就让j回退到-1直接从主串的下一个点和模式串的第一个点进行匹配*/++i;++j;}elsej = next[j];//如果失匹模式串回退到k位置继续进行匹配(即:next[j]位置)if(j >= m)//如果j匹配到m,说明主串在模式串中完全匹配成功,模式串在主串中的起始下标就是i-m(第i-m+1个位置从零开始数就是i-m从1开始数就是i-m+1)return i - m;}return 0;}


个人感觉如果理解了kmp主串和模式串匹配的原理,next数组的求解就比较好理解了,因为next数组的求解过程就是模式串自我匹配的过程


/*求next数组其实就是模式串自我匹配的过程,*/void get_next(char b[], int m)//b数组为模式串,模式串长度{int i = 0, j = -1;next[0] = -1;//第一个位置前面不可能出现k个元素匹配所以直接回退到-1while(i < m){if(j == -1 || b[i] == b[j]){++i;++j;next[i] = j;//从0到j-1位置肯定和i-k到i-j位置是匹配的所以如果模式串在i位置失匹只需要回退到j位置就可以了/*举个板栗,假设当前的j为jk,如果前jk不匹配,那么在某个位置j就回退了,j根本自加不到jk...*/}else//同主串和模式串匹配时的回退原理,回退到next【j】(k)位置继续进行匹配j = next[j];}}



如果模式串为“aaaab”主串为“aaabaaaab”匹配时,当i = 3,j = 3时a【i】≠ b【j】,由前面计算的next还需要进行i = 3(j = 2,j = 1, j = 0)三次多余的比较,


next[j] = k; 而模式串中bj = bk,当主串中字符ai和pj比较不想登时,不需要再和bk进行比较,而直接和b【next【k】】进行比较,此时的next【j】和next【k】应该相同

所以next数组的求解可以改进成


void get_next(char b[], int m)`{int i = 0, j = -1;next[0] = -1;while(i < m){if(j == -1 || b[i] == b[j]){++i;++j;if(a[i] == b[j])next[i] = next[j];elsenext[i] = j;}elsej = next[j];}}