数据结构实验四 串的模式匹配BF/KMP


#include <iostream>
#include "stdio.h"
#include "stdlib.h"
#include "cstdlib"//syste()函数需要该头文件;

using namespace std;

#define OK 1
#define ERROR 0
//#define OVERFLOW -2
typedef int Status;

typedef char SElemType;

#define MaxLen 255 // 用户可在255以内定义最大串长,超出这个长度则超出部分被舍去,称为截断


//00 _栈:typedef char SString[MaxLen+2];//0不用,最后一个\0

//00 _堆:串的堆顺序存储,动态分配,结构无数据域,需创建或初始化中开辟空间

typedef struct {
  SElemType *ch; //若串非空,则按串长分配存储区,否则ch为NULL
  int length; //串长度
}HString;//heap堆,故HString,SString?

//01初始化串 P58 0 1...maxlen \0 ,多两个空间不放字符
Status InitHString(HString &S)
{
  S.ch = (char*)malloc((MaxLen + 2) *sizeof(char));
  if (!S.ch)
    exit(OVERFLOW);
  S.ch[1] = '\0';
  S.length = 0;
  return OK;
}

//02创建串,从键盘录入<=MaxLen
Status CreateHString(HString &S)
{
  if (!S.ch)
    InitHString(S);

  int i = 1;
  char c;
  while (i <= MaxLen)
  {
    cin >> c;
    if (c<='z'&&c>='a')
      {
        S.ch[i]=c;
        i++;
      }
    else
      break;
  }
  S.ch[i] = '\0';
  S.length = i-1;
  return OK;
}

//03 BF算法
int Index_BF(HString S, HString T) //返回模式串T在主串S中第一次出现的位置。因返回的是位置,不是状态,虽都是int,但不用Status,另说清谁是模式串,谁是主串。

{
  int i = 1, j = 1, sum = 0; // i 指示主串S中进行匹配字符的位置,j 指示主串T中进行匹配字符的位置;sum累计比较次数;
  while (i <= S.length&&j <= T.length)//看上面例子分析得出,匹配只有成功和不成功两种情形,换成计算机语言也就是if else;
  {
    sum++;
    if (S.ch[i] == T.ch[j]) //看上面例子,不管那一轮只要成功,那么有i++ ; j++
    {
      i++;
      j++;
    }
    else //看上面例子,不管那一轮只要失败,那么有i回溯到初值的下一位; j 始终是第1个和Si,即j=1;
    {
      i = i - j + 2; //i=i-(j-1)+1,看上面例子中的解释;
      j = 1;
    }
  }//循环完也只有两种情形,要么匹配,要么失败,换成计算机语言仍就是if else;

  cout << "BF一共比较了" << sum << "次" << endl;
  if (j>T.length)
    return (i - T.length); //返回位置. 比较成功的次数是T.length, i从初值也后移了T.length,i-T.length回到初值
  else
    return 0;
}

//04 Next函数
void Get_Next(HString T, int next[])//求模式串T的next[]函数值,其实是知next[1],求next[2],依次……,也即假设已知next[j], ++j后求得next[j]即未我所求;
{
  next[0] = T.length;
  next[1] = 0; //初值
  int k, j;
  j = 1; // j 代表的是后缀末尾的下标, 假设next[j]已知,则 ++j 后,next[j]即是所求;
  k = 0; // k代表的是前缀结束时的下标,也就是 j 前有k个字符的前缀T1T2...Tk等于后缀Tj-m+1...Tj
  while (j < T.length)
    {

      if (k == 0 || T.ch[k] == T.ch[j]) //1. j>=2时, 怎么求next[j+1]?2. 当j=1时,怎么求next[j+1]?
        {

          ++j;
          ++k;
          next[j] = k; //++j后才是我们想要的next[j],++k后才是我们想要给next[j]赋的值;一定要先++
        }
      else//匹配失败的情况,就要进行回溯,下一轮tj与tk'比较,j不动,k'=next[k];
        k = next[k];
    }
}

//05 Nextval函数
void Get_Nextval(HString T, int nextval[])//求模式串T的nextval[]函数值,在next函数的基础上只需改动tj=tk相等时的情形,让nextval[j]=nextval[k]而不是k ;
{
  nextval[0] = T.length;
  nextval[1] = 0; //初值
  int k, j;
  j = 1; // j 代表的是后缀末尾的下标, 若next[j]已知,则 ++j 后,next[j]即是所求;
  k = 0; // k代表的是前缀结束时的下标,也就是 j 前有k个字符的前缀T1T2...Tk等于后缀Tj-m+1...Tj
  while (j < T.length)
  {
    if (k == 0 || T.ch[k] == T.ch[j]) //1. 当j=1时,怎么求next[2],也即next[++j]?2. j>=2时,已知next[j], 怎么求next[j+1]?
      {
        ++j;
        ++k;
        if (T.ch[k] == T.ch[j])
          nextval[j] = nextval[k]; //相等了意味着Si无需和Tk在比较了,回溯到k的上一个位置k'=next[k],让Si和Sk'比较
        else
          nextval[j] = k; //T.ch[k]!=T.ch[j],相当于nextval[++j]=++k,也即nextval[j]=next[j]=k,此情形同next[]函数;
      }
     else//匹配失败的情况,就要进行回溯,下一轮tj与tk'比较,j不动,k'=next[k];
        k = nextval[k];
  }
}

//06 KMP,到底是next还是nextval,看主函数调用该子函数传的参数是哪一个
int Index_KMP(HString S, HString T, int next[])//利用非空模式串T的next函数求T在主串S中的位置的KMP算法,形式上近同BF算法
{
  int i = 1,j = 1, sum = 0;//i-->S,j-->T,sum-->循环次数
  while (i <= S.length&&j <= T.length)
    {
      sum++;
      if (j == 0 || S.ch[i] == T.ch[j]) // 继续比较后面的字符
        {
          i++;
          j++;
        }
      else
        j = next[j]; // 利用next函数确定下一次模式串中第j个字符与T.ch[i]比较,i不变
    }
  cout << "KMP一共比较了" << sum << "次" << endl;
  if (j>T.length) // 匹配成功
    return i - T.length;
  else
    return 0;
}

//07输出串
void PrintStr(HString Str)//输出串
{
  for (int i = 1; i <= Str.length; i++)
  {
    printf("%c ", Str.ch[i]);
  }
  printf("\n");
}
//08打印next值,主函数调用该函数,传的参数是next[],则打印next[j],传nextval[],则打印nextval[j], 该子函数中的next[]只是一个形参,可以起别的名
void PrintNext(int next[])
{
  for (int k = 1; k <= next[0]; k++)
  {
    printf("%d ", next[k]);
  }
  printf("\n");
}

void main()

{

HString S, T; //S主串,T子串; 
int result = -1;//匹配结果 
char ch;
cout<<"S:"<<InitHString(S)<<endl;
cout << "T:" << InitHString(T) << endl;
getchar();//注意学会一步步调试,先看初始化对不对,在看创建,在看BF,next,nextval,KMP,一步一步来调试
/*
do {
  system("cls");//一般不需要头文件,(#include<windows.h>)clrcsr()
  cout << "输入主串:";
  CreateHString(S);
  PrintStr(S);

  cout << "输入子串:";
  CreateHString(T);//测试书本P97 图4.9 t="aaaab";
  PrintStr(T);

  cout << "------BF------"<<endl;;
  result = Index_BF(S, T);
  if (result != -1)
    cout << "BF:主串与子串在主串的第" << result << "个字符(首字符的位置为1)处首次匹配" << endl;
  else
    cout << "BF:无匹配子串" << endl;

  int *next =new int[T.length+1];//next[0]用于存放模式串的长度,next[j] j=1开始;
  Get_Next(T,next);
  cout << "模 式 串:";
  PrintStr(T);
  cout << "n e x t[j]:";
  PrintNext(next);

  int *nextval = new int[T.length + 1];
  Get_Nextval(T,nextval);
  cout<<"nextval[j]:";
  PrintNext(nextval);

  cout << "------KMP_NEXT------" << endl;
  result = Index_KMP(S, T, next);
  if (result != -1)
    cout << "KMP_NEXT:主串与子串在主串的第" << result << "个字符(首字符的位置为1)处首次匹配" << endl;
  else
    cout << "KMP_NEXT:无匹配子串" << endl;

  cout << "------KMP_NEXTVAL------" << endl;
  result = Index_KMP(S, T, nextval);
  if (result != -1)
    cout << "KMP_NEXTVAL:主串与子串在主串的第" << result << "个字符(首字符的位置为1)处首次匹配" << endl;
  else
    cout << "KMP_NEXTVAL:无匹配子串" << endl;

  cout <<"是否继续测试(输入y或Y继续,任意其他键结束):";
  cin >> ch;
} while (ch == 'y' || ch == 'Y');
*/
system("pause");

}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM