字符串匹配算法

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/AMDDMA/article/details/87873223

字符串匹配算法有:暴力匹配、KMP、BM

KMP算法

        先计算出要匹配的字符串的特征数值(字符串首尾出现频率高的字符串最长长度),依据特征值指导字符跳转。

        

BM算法

        BM算法和KMP算法的差别是对模式串的扫描方式自左至右变成自右至左。另一个差别是考虑正文中可能出现的字符在模式中的位置。这样做的好处是当正文中出现模式中没有的字符时就可以将模式大幅度滑过正文。

        BM算法的关键是根据给定的模式W[1,m],,定义一个函数d: x->{1,2,…,m},这里x∈∑。函数d给出了正文中可能出现的字符在模式中的位置。

参考:https://www.cnblogs.com/huifeidezhuzai/p/9222366.html

KMP匹配算法

大致匹配流程

KMP依赖数据:

1、查找对象字符串的特征值分析,所谓的特征值就是字符出现频率高的字符串最长长度,以数字表示频率值;

2、源字符串、查找对象字符串;

上图中查找对象字符串的特征值计算方式如下:

直接(暴力)匹配算法

//暴力匹配算法
char* strstr_cpy(char src[],char des[])
{
  int length_src = strlen(src);
  int length_des = strlen(des);
  int i = 0;
  int j = 0;

  if(length_des <= 0 || length_src <= 0)
  {
    return 0;
  }

  while(i<length_src)/* 遍历源字符串 */
  {
    while(j<length_des)/* 遍历目标字符串 */
    {
      if(src[i+j] == des[j])  /* 如果当前位置的字符和目标字符相同,则持续往后移动 */
      {
        j++;
      }
      else      /* 如果有任意一个字符不匹配,则重新开始 */
      {
        i++;
        j = 0;
      }
    }
    if(j >= length_des)
    {
      return src+i;
    }
  }
  return NULL;
}

KMP算法实现与优化

void old_get_next(char src[],int next[])
{
  int m = strlen(src);
  int i=0;

  for(i=1;i<m;i++)//遍历字符串
  {
      int j=i;    //从当前位置开始遍历
      while(j>0)  
      {
        j=next[j];      
        if(src[j]==src[i])  //如果当前位置的字符等于前面的字符
        {
          next[i+1]=j+1;
          break;
        }
      }
   }
}

char* kmp_test(char src[],char des[])
{
  int length_src = strlen(src);
  int length_des = strlen(des);
  int next[length_des];
  int i = 0;
  int j = 0;
  int k=0;

  if(length_des <= 0 || length_src <= 0)
  {
    return 0;
  }

  memset(next,0,sizeof(next));

  /* 获取特征值 */
  old_get_next(des,next);

  while(i<length_src)/* 遍历源字符串 */
  {
    while(j<length_des)/* 遍历目标字符串 */
    {
      if(src[i+j] == des[j])  /* 如果当前位置的字符和目标字符相同,则持续往后移动 */
      {
        j++;
      }
      else      /* 如果有任意一个字符不匹配,则重新开始 */
      {
        i += 1 + next[j];
        j = 0;
      }
      kmp_countor++;
    }
    if(j >= length_des)
    {
      return src+i;
    }
  }
  return NULL;
}

算法优化:

         KMP的next数组计算和优化,待补充完善。

实际应用算法

#include <stdio.h>
#include <string.h>
#include <sys/time.h>

int str_countor = 0;
int kmp_countor = 0;

//暴力匹配算法
char* strstr_cpy(char src[],char des[])
{
  int length_src = strlen(src);
  int length_des = strlen(des);
  int i = 0;
  int j = 0;

  if(length_des <= 0 || length_src <= 0)
  {
    return 0;
  }

  while(i<length_src)/* 遍历源字符串 */
  {
    while(j<length_des)/* 遍历目标字符串 */
    {
      str_countor++;
      if(src[i+j] == des[j])  /* 如果当前位置的字符和目标字符相同,则持续往后移动 */
      {
        j++;
      }
      else      /* 如果有任意一个字符不匹配,则重新开始 */
      {
        i++;
        j = 0;
      }
    }
    if(j >= length_des)
    {
      return src+i;
    }
  }
  return NULL;
}

void old_get_next(char src[],int next[])
{
  int m = strlen(src);
  int i=0;

  for(i=1;i<m;i++)//遍历字符串
  {
      int j=i;    //从当前位置开始遍历
      while(j>0)  //
      {
        j=next[j];      //初始值为
        if(src[j]==src[i])  //如果当前位置的字符等于前面的字符
        {
          next[i+1]=j+1;
          break;
        }
      }
   }
}

char* kmp_test(char src[],char des[])
{
  int length_src = strlen(src);
  int length_des = strlen(des);
  int next[length_des];
  int i = 0;
  int j = 0;
  int k=0;

  if(length_des <= 0 || length_src <= 0)
  {
    return 0;
  }

  memset(next,0,sizeof(next));

  /* 获取特征值 */
  old_get_next(des,next);

  while(i<length_src)/* 遍历源字符串 */
  {
    while(j<length_des)/* 遍历目标字符串 */
    {
      if(src[i+j] == des[j])  /* 如果当前位置的字符和目标字符相同,则持续往后移动 */
      {
        j++;
      }
      else      /* 如果有任意一个字符不匹配,则重新开始 */
      {
        i += 1 + next[j];
        j = 0;
      }
      kmp_countor++;
    }
    if(j >= length_des)
    {
      return src+i;
    }
  }
  return NULL;
}

int main()
{
  char src[]="ABDFABDABEABDFHABDFABDABEABDFHGDGDABDFABDABEABAFHGDABDFABDABEABDFHABDFABDABEABDFHGDGDABDFABDABEABDFHGDABDFABDABEABDFHABDFABDABEABDFHGDGDABDFABDABEABEFHGDABDFABDABEABDFHABDFABDABEABDFHGDGDABDFABDABEABcFHGD";
  char des[]="ABEABc";

  struct timeval start, end;
  gettimeofday(&start, NULL);

  printf("src长度:%d des的长度为:%d\n",strlen(src),strlen(des));
  printf("[str_cpy] 共比较%d次,strstr结果是:%s!\n",str_countor,strstr_cpy(src,des));
  printf("[KMP]  共比较%d次,kmp结果是:%s !\n",kmp_countor,kmp_test(src,des));
  gettimeofday(&end, NULL);
  long long total_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec);

  printf("运行时间长度;%lldus\n",total_time);

}

算法结果分析

        由于linux的调度会影响代码运行时间的计算,因此实际的运算以比较的次数为准,其中KMP的比较次数为计入扫描des字符串的时间,加上des的字符串扫描消耗时间,KMP的计算次数相当于311次,低于str直接比较。

左右开工形式的匹配算法

将BM和KMP结合的算法

待补充完善

展开阅读全文

没有更多推荐了,返回首页