#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");
}
