经典算法kmp,大神级的存在
对于字符串匹配,如用一个长度为m的模式串去匹配一个长度为n的主串,我们可以使用暴力法(BF,Brute Force),其时间复杂度为O(m*n)。
字符串匹配kmp算法的时间复杂度只需要O(m+n)。
使用变量 i, j 表示主串s[]和模式串t[]的下标, 第一轮匹配:
kmp算法认为,i 可以不回退(BF要求退回到主串的第2个位置(第几轮就是第几个位置)),而 j 可以回退到第2个位置,即 j=2(BF要求退回到每1个字符的位置)。
即使使用暴力破解,几轮迭代后,也会迭代匹配到此位置,所以上述 i,j 的回退不影响整体结果的正确性。
模式串此时回退为什么是2呢?
看下面已匹配的公共部分:
主串以不匹配的位置为基准,考虑前面的字符abaab,有后缀ab与模式串abaabe最前面的字符前缀ab相等。
也就是模式串本身abaab,有最大后缀部分ab与最大前缀ab相等,相等字符的长度为2。
从上图可见,假设一个字符串长度为n,其最大前缀和后缀相等的字符数量不会超过n/2,例如:
abcdabcd,长度为8,8/2=4,其最大前缀和后缀相等的字符串abcd最大可能的长度为4。
如何暴力计算下面字符的最大前缀和后缀字符串的长度L呢?
abcdaabbcdeabcd,长度n为15,其L不会超过n/2=7,暴力匹配的思路可以描述为:前1个字符与后1个字符是否相等?
前2个字符与后2个字符是否相等?
前3个字符与后3个字符是否相等?
……
前n/2个字符与后n/2个字符是否相等?
暴力匹配的思路也可以描述为:
前n/2=7个字符与后n/2=7个字符是否相等?
abcdaab bcdeabcd
前n/2-1=6个字符与后n/2-1=6个字符是否相等?
abcdaa bbc deabcd
前n/2-2=5个字符与后n/2-2=5个字符是否相等?
abcda a bbcd eabcd
abcdaa bbc deabcd
前n/2-3=4个字符与后n/2-3=4个字符是否相等?
abcd aa bbcde abcd
此时相等,则L为4。
对于模式串abaabe,如何计算各个子串对应的最大前缀与后缀字符串数量(j回退到的位置)?
ab aab
2
a baa
1
a ba
1
ab
0
a
-1
图示:
对应代码:int *getNextArray(char t[]) // 动态规划 { int n = strlen(t); int *next = (int*)malloc(sizeof(int)*n); next[0] = -1; next[1] = 0; int k; for (int j = 2; j < n; j++) { k=next[j-1]; // 先假设 while (k!=-1) { if (t[j - 1] == t[k]) { next[j] = k + 1; break; }else k = next[k]; // 回退 next[j] = 0; //当k==-1而跳出循环时,next[j] = 0,否则next[j]会在break之前被赋值 } } return next; }
对于模式串T的下标 j 回退位置next[]的求解方法,KMP算法应用的动态规划的思想:
首先大胆假设next[j]=k,则
那么next[j+1]=?
也就是以上子串再分别多考虑一个随后的字符:
可以区分这两个字符在相等和不等的情况下分别考虑:
有了确定模式串回退位置的数组,字符串匹配剩下的代码就相对较容易了。
demo c code:#include #include #include // 对主串s和模式串t进行KMP模式匹配 // 计算模式串t需要回退的位置(BF是回退到0) int *getNextArray(char t[]) // 动态规划 { int n = strlen(t); int *next = (int*)malloc(sizeof(int)*n); next[0] = -1; next[1] = 0; int k; for (int j = 2; j < n; j++) { k=next[j-1]; // 先假设 while (k!=-1) { if (t[j - 1] == t[k]) { next[j] = k + 1; break; }else k = next[k]; // 回退 next[j] = 0; //当k==-1而跳出循环时,next[j] = 0,否则next[j]会在break之前被赋值 } } return next; } /** * 对主串s和模式串t进行KMP模式匹配 * @param s 主串 * @param t 模式串 * @return 若匹配成功,返回t在s中的位置(第一个相同字符对应的位置),若匹配失败,返回-1 */ int kmpMatch(char* s, char* t){ int *next = getNextArray(t); int i = 0, j = 0; while (i<(int)strlen(s) && j<(int)strlen(t)){ if(j == -1 || s[i]==t[j]){ i++; j++; } else j = next[j]; } //printf(" i-j = %d - %d = %d ",i,j,i-j); if(j == (int)strlen(t)) return i-j; else return -1; } int main() { //char* str[] = {"ACBACAACAACACAACAB","ACAACAB"}; //char* str[] = {"abaabaabeca","abaabe"}; char* str[] = {"abaabaeabaabea","abaabe"}; int *next = getNextArray(str[1]); int i,j; printf("主串s[]= "); for(i=0;i<(int)strlen(str[0]);i++) printf("%c ",str[0][i]); printf(" "); for(i=0;i<2;i++) { printf("模式串t[]= "); for(j=0;j<(int)strlen(str[1]);j++) printf("%c ",str[1][j]); printf(" "); } printf("next[] = "); for(i=0;i
年薪1218w!三钢下属国企,本科起报!福建罗源闽光钢铁有限责任公司成立于2014年8月,为国有上市企业福建三钢闽光股份有限公司全资子公司。三钢集团历经60年发展,已形成年产钢1200万吨和以钢铁业为主多元产业并举的跨行
此去1997死都忘不了的青春记忆1997年,死都忘不了的青春记忆。那年似乎发生了很多大事,但留在印象里的也只有香港回归了。毕竟这是一个民族百年的夙愿,一个民族的强大复兴是让每一个属于这个民族的人值得骄傲和自豪的。
故乡的记忆故乡的记忆郭永明编辑于20159122132朋友的几幅照片,撬开了我对故乡的回忆。我的故乡在黄土高原的一个村落。贫脊和干涸镶嵌在我童少年的记忆。茫茫荒原,时而可见的几棵老榆树,挣扎
一起来看看,这个美丽乡村的自建房有多美!绿水青山屋舍炊烟,农家屋场尽显风味,眼前一幅田园好风光。各式各样的美丽乡村房小屋,让这个美丽乡村处处都是景!大家一起来看看,湖南省张家界市永定区南庄坪街道二家河社区乡村自建房,有多
乡村变美村民增收四川南充推动乡村旅游提质升级出实招封面新闻记者杨金祝近日,由四川省文化和旅游厅主办的2023年南充文旅主题宣传月新闻通气会在南充高坪区正式启动。活动围绕安逸四川春满南充的主题,展示和推介南充春季旅游特色资源,同时发
福建建宁云雾缭绕乡村美雨后的建宁县黄坊乡美如画(央广网发游海飞摄)建宁县黄坊乡(央广网发游海飞摄)黄坊乡在雨雾中若隐若现(央广网发游海飞摄)央广网三明3月25日消息(记者罗晓英通讯员游海飞)3月24日,
春和景明山水秀乡村生态美如画央视网消息春天里的中国,春耕忙,春意浓。接下来,我们就一起去各地看看。山西吉县山花烂漫满山坡竞相吐蕊争芳艳现在我们来到山西临汾市吉县南村坡村。随着气温回升,漫山遍野的山桃杏花竞相吐
第六届济宁(泗水)乡村旅游美食节开幕!阳春三月,泗水大地春光明媚,鲜花怒放,处处洋溢着浓浓的芳香,3月25日,第六届济宁(泗水)乡村旅游美食节启动仪式在泗水万紫千红生态旅游度假区隆重举行。本届美食节由济宁市文化和旅游局
使命第三章重生在暗夜中经历无数的挣扎,我始终无法战胜黑暗,因为靠自己,是不足以抵挡那背后的神秘力量的。于是父神让我来到人世间历练,以助我修心得圆满。这一世的人间炼狱般的生活,真正开始于
父亲突然倒下之后,生活让我接受了现实父亲突然倒下之后,生活让我接受了现实我想到这个梦,以为自己已经慢慢接受现实,但其实并没有。因为不论吃到什么,看到什么,我都在想要是爸爸在就好了。前言在我儿时的记忆里,没有寻常人家家
2023年再读百岁杨绛清澈8句话,烦恼全放下1hr把圈子变小,把语速放缓,把心放宽,把生活打理简单,用心做好手边事情不恋尘世浮华,不写红尘纷扰看天上的月,吹人间的风。过平常的日子,该有的,总会有。阅读杨绛传,摘录下来这样一段