數據結構實驗四 串的模式匹配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