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