PART ONE——实现
#include<stdio.h>
#include<stdlib.h>
#include <time.h>
#include<windows.h> //头文件
#define Time_init LARGE_INTEGER lFrequency; \
QueryPerformanceFrequency(&lFrequency);
#define Time_begin LARGE_INTEGER lBeginCount; \
QueryPerformanceCounter(&lBeginCount);
#define Time_end LARGE_INTEGER lEndCount; \
QueryPerformanceCounter(&lEndCount);
#define Time_out double time = (double)(lEndCount.QuadPart - lBeginCount.QuadPart) / (double)lFrequency.QuadPart; \
printf("运行时间%lf毫秒\n",time*1000);
#define NO_FIND -1
#define FIRST -1
typedef struct string {
char * ch;
int length;
}HString;
const char * S = "acabaabaabcacaabc"; //给定主字符串
HString * init(int len)
{
HString * p = (HString *)malloc(sizeof(HString));
if (p == NULL)
{
printf("初始化失败!退出程序!\n");
exit(-1);
}
p->length = len;
p->ch = (char *)malloc(sizeof(char) * (p->length + 1));
printf("输入子字符串:");
scanf("%s", p->ch); //输入字符串
*(p->ch + p->length) = '\0'; //作为字符串的结束标志
return p;
}
//朴素模式匹配
int moshipipei(const char * S, HString * T) //返回匹配到的第一个的坐标,S为主串,T为子串
{
int i = 0; //主串指示器
int j = 0; //子串指示器
while (*(S + i) != '\0' && *(T->ch + j) != '\0') //前提是两个字符串都不为空
{
if (*(S + i) == *(T->ch + j))
{
i++; //若相等则指示器往后移
j++; //
}
else //若不相等,主串后退到这一趟开始的地方然后前移一个单位,子串回到初始位置 ; 进行下一趟比较
{
i = i - (j - 1);
j = 0;
}
}
if (j == T->length)
{
return (i - j);
}
return NO_FIND; //没找到,用 NO_FIND 与如果在0出就匹配到区分来
}
/**************************************************************/
//确定 next[];(待改进)
int * get_next(HString * T)
{
int * next = (int *)malloc(sizeof(int) * T->length); //用来保存next[]的值
int k = FIRST;
*next = FIRST;
for (int i = 1; i < T->length; i++) //每一轮开始的k都是上一次next的值
{
while (k > -1)
{
if (*(T->ch + i - 1) == *(T->ch + k))
{
k += 1;
break;
}
else
{
k = *(next + k); //回溯
}
}
if(k == FIRST)
k = 0;
*(next + i) = k;
}
return next;
}
//KMP get next[](改进)
int * get_nextval(HString * T)
{
int * next = (int *)malloc(sizeof(int) * T->length); //用来保存next[]的值
int k = -1;
*next = -1;
int i = 0;
while (i < T->length)
{
if (k == -1 || *(T->ch + i) == *(T->ch + k))
{
i++;
k++;
if (*(T->ch + i) != *(T->ch + k))
{
next[i] = k;
}
else
{
next[i] = next[k];
}
}
else
{
k = next[k];
}
}
return next;
}
//KMP算法
int KMP(const char * S, HString * T)
{
int * next = get_nextval(T);
int k = 0;
printf("next数据分别为:\n");
while (k < T->length)
{
printf("%d\t", *(next + k++));
}
printf("\n");
int i = 0;
int j = 0;
while (*(S + i) != '\0' && *(T->ch + j) != '\0') //前提是两个字符串都不为空
{
if (*(S + i) == *(T->ch + j) || j == -1)
{
i++; //若相等则指示器往后移
j++; //
}
else //若不相等,i 不变, j 滑动窗口
{
j = next[j];
}
}
if (j == T->length)
{
return (i - j);
}
return NO_FIND; //没找到,用 NO_FIND 与如果在0出就匹配到区分来
}
/****************************************************************/
int main()
{
int val;
int pos;
Time_init;
printf("输入子字符串长度:");
scanf("%d", &val);
HString * T = init(val);
printf("%s\n", T->ch);
printf("现在进行模式匹配:\n");
Time_begin;
//pos = moshipipei(S, T);
pos = KMP(S, T);
Time_end;
Time_out;
if (pos == NO_FIND)
{
printf("并没有在主串中找到子串!");
}
else
{
printf("在主串中匹配到子串,满足的字符串在主串中的第一个字符位置为(从0开始):%d", pos);
}
return 0;
}
PART TWO——朴素算法
//朴素模式匹配
int moshipipei(const char * S, HString * T) //返回匹配到的第一个的坐标,S为主串,T为子串
{
int i = 0; //主串指示器
int j = 0; //子串指示器
while (*(S + i) != '\0' && *(T->ch + j) != '\0') //前提是两个字符串都不为空
{
if (*(S + i) == *(T->ch + j))
{
i++; //若相等则指示器往后移
j++; //
}
else //若不相等,主串后退到这一趟开始的地方然后前移一个单位,子串回到初始位置 ; 进行下一趟比较
{
i = i - (j - 1);
j = 0;
}
}
if (j == T->length)
{
return (i - j);
}
return NO_FIND; //没找到,用 NO_FIND 与如果在0出就匹配到区分来
}
PART THREE——KMP算法
//KMP算法
int KMP(const char * S, HString * T)
{
int * next = get_nextval(T);
int k = 0;
printf("next数据分别为:\n");
while (k < T->length)
{
printf("%d\t", *(next + k++));
}
printf("\n");
int i = 0;
int j = 0;
while (*(S + i) != '\0' && *(T->ch + j) != '\0') //前提是两个字符串都不为空
{
if (*(S + i) == *(T->ch + j) || j == -1)
{
i++; //若相等则指示器往后移
j++; //
}
else //若不相等,i 不变, j 滑动窗口
{
j = next[j];
}
}
if (j == T->length)
{
return (i - j);
}
return NO_FIND; //没找到,用 NO_FIND 与如果在0出就匹配到区分来
}
PART FOUR——KMP算法中next[]数组求解
//确定 next[];(待改进)
int * get_next(HString * T)
{
int * next = (int *)malloc(sizeof(int) * T->length); //用来保存next[]的值
int k = FIRST;
*next = FIRST;
for (int i = 1; i < T->length; i++) //每一轮开始的k都是上一次next的值
{
while (k > -1)
{
if (*(T->ch + i - 1) == *(T->ch + k))
{
k += 1;
break;
}
else
{
k = *(next + k); //回溯
}
}
if(k == FIRST)
k = 0;
*(next + i) = k;
}
return next;
}
这是刚开始我直观的写出来,但是如果是字符串’aaaab’则获得的next[]数组值分别为’-1 0 1 2 3’,里面有许多事没必要的,就像主串为’aaabaaaab’时,i = 4,j = 3之后还要进行 i = 4, j = 2, 直到 i = 4,j = 0,这些步骤明显有些多余,但是我自己写的next求解算法我还无法进行优化,所以我用书本上的伪代码写出了后面的优化代码。
//KMP get next[](改进)
int * get_nextval(HString * T)
{
int * next = (int *)malloc(sizeof(int) * T->length); //用来保存next[]的值
int k = -1;
*next = -1;
int i = 0;
while (i < T->length)
{
if (k == -1 || *(T->ch + i) == *(T->ch + k))
{
i++;
k++;
if (*(T->ch + i) != *(T->ch + k))
{
next[i] = k;
}
else
{
next[i] = next[k];
}
}
else
{
k = next[k];
}
}
return next;
}