KMP算法与string::find以及strstr的比较

首先,简单描述一下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

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/bertzhang/article/details/7291819
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞