KMP演算法與string::find以及strstr的比較
阿新 • • 發佈:2019-02-03
首先,簡單描述一下KMP演算法,要理解好KMP演算法,最好參考演算法導論[1],尤其是先理解好自動機匹配的方法,再看KMP就很容易理解了。它利用的是一個關鍵的回退原理,也就是如果匹配失敗時,那麼我知道只要從模式的某個位置繼續匹配就可以了,這個回退的位置事先通過模式計算出來,也就是說如果某個位置匹配不成功,就回退到事先算好的位置,繼續匹配。這個事先算好的位置就是從0到該位置應該是匹配不成功處的一個字尾。這個過程可以通過已經算好的回退位置來遞推出來。
從演算法的描述看,回退的陣列的構建與匹配的過程是非常相似的。
這裡比較了KMP和string::find以及strstr的效能,程式碼如下:
#include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <sys/time.h> #include <string.h> void ComputePrefixFun(const std::string& patten, std::vector<int>* back) { int k = -1; (*back).push_back(-1); for (int i = 1; i < patten.size(); ++i) { while (k > -1 && patten[k + 1] != patten[i]) { k = (*back)[k]; } if (patten[k + 1] == patten[i]) { k++; } (*back).push_back(k); } } void Match(const std::string& src, const std::string& patten, const std::vector<int>& back, std::vector<size_t>* offset) { int k = -1; size_t src_length = src.size(); size_t patten_length = patten.size(); for (int i = 0; i < src_length; ++i) { while (k > -1 && patten[k + 1] != src[i]) { k = back[k]; } if (patten[k + 1] == src[i]) { k++; } if (k == patten_length - 1) { (*offset).push_back(i - patten_length + 1); k = back[k]; } } } std::string IntToString(int value) { std::string res = ""; do { res.insert(0,std::string(1, value % 10 + '0')); } while ((value /= 10) >0); return res; } int GetTime() { timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000000 + tv.tv_usec; } int main(int argc, char** argv) { std::string patten = "1212123"; std::vector<int> back; std::string src = ""; for (int i = 0; i < 1000000; ++i) { src += IntToString(rand()); } ComputePrefixFun(patten, &back); std::vector<size_t> offset; int start = GetTime(); Match(src, patten, back, &offset); printf("time:%d %zd\n", GetTime() - start, offset.size()); start = GetTime(); int index = -1; int count = 0; while ((index = src.find(patten, index + 1)) != std::string::npos) { count++; } printf("time:%d count = %d\n", GetTime() - start, count); const char* pos = src.c_str(); start = GetTime(); count = 0; while (true) { pos = strstr(pos, patten.c_str()); if (pos == NULL) break; pos++; count++; } printf("time:%d find:%d \n", GetTime() - start, count); std::string matched = ""; for (int i = 0; i < offset.size(); ++i) { matched = src.substr(offset[i], patten.size()); // printf("%zd %s ", offset[i], matched.c_str()); } printf("\n"); }
開始編譯時沒有使用優化選項,結果KMP的效能總是不理想,使用-O3編譯後,效能明顯改善了。
參考文獻
演算法導論 第32章 P586