这次的代码写了好久。。因为有一个问题一直没想透彻。要实现子列相对主列的相对移动,要移动的是子列的指针。。。我一直以为是主列需要一个缩进指针,所以改了好久。。更悲催的是前几次测试阴差阳错的全都对了呃呃呃呃。。后来就发现测试有问题。。。最后才想到。。
另外,在计算前缀值的那一步,我本来写了一个char***类型的数组,是一个char*类型的二维数组,想存子列各个前缀的内容。但是后来发现,第一因为存的是指针,读的时候也是读的最初那块内存(main函数里的内存),所以即使给char**malloc一块空间,存上’\0’,也并不能在读取的时候读取到存进去的截断后的字符串,只能读到最原始的字符串。。。也就是说,我通过复制地址的方式,想存把abcd截断,存成ab’\0’,但是因为读的是最原始的内存地址,一定还是abcd。第二,完全可以通过其他方式实现了这个功能。
本代码在code::blocks17.12中运行正常。
#include <stdio.h>
#include <stdlib.h>
typedef struct prefix_table{
char** data;//一个char*类型的数组。不复制子字符串的内容,仅把子字符串的指针复制紧螺丝
int* prefix;//最长公共子列长度的数组
int size;//子列长度
}table;
//根据目标字符串建立一个前缀表的框架
table* create_table(char* part){
int len=string_length(part);
table* newtable=(table*)malloc(sizeof(table));
newtable->size=len;
newtable->data=(char**)malloc((len+1)*sizeof(char*));
newtable->prefix=(int*)malloc((len+1)*sizeof(int));
for(int i=0;i<=len;i++){
newtable->data[i]=&part[i];
newtable->prefix[i]=0;
}
newtable->prefix[0]=-1;
return newtable;
}
//KMP算法本身
int KMP(char* whole,char* part){
table* t=create_table(part);//根据目标字符串建立一个前缀表的框架
calculate_prefix(t);//给前缀表填入计算的前缀值
print_prefix_table(t);//输出前缀表
int pwhole=0;
int pt=0;
int temp;
int len=string_length(whole);//whole数组长度(计算结束点要用)
while(t->data[pt][0]!='\0'){
printf("pwhole=%d\n",pwhole);
if(whole[pwhole]==t->data[pt][0]){
printf("主[%d]%c == 子[%d]%c\n",pwhole,whole[pwhole],pt,t->data[pt][0]);
pwhole++;
pt++;
}else{
printf("主[%d]%c != 子[%d]%c\n---------\n",pwhole,whole[pwhole],pt,t->data[pt][0]);
temp=t->prefix[pt];
if(temp==-1){
if(pwhole>=len){//当起始位置都不足以把part遍历一遍了,就一定不包含了
return 0;//不包含
}
pwhole++;
continue;
}
pt=temp;
if(pwhole>=len){//当起始位置都不足以把part遍历一遍了,就一定不包含了
return 0;//不包含
}
}
}
return 1;//包含
}
//给前缀表填入计算的前缀值
void calculate_prefix(table* t){
for(int i=1;i<t->size;i++){
t->prefix[i]=longest_common_prefix(t->data[0],i);//把填入table中的字符串按不同长度(i)分别计算最长公共子列,并放入table->prefix中
}
}
//创建一个字符串(s)的子字符串
char* create_substring(char* s,int start,int end){
int len=string_length(s);
if(start>s||end>s){
return -1;
}
char* res=malloc((end-start+1)*sizeof(char));
int res_i=0;
for(int i=start;i<=end;i++){
res[res_i++]=s[i];
}
res[res_i]='\0';
return res;
}
//求一个字符串(s)在不同输入长度(len)下的的最长公共子列
int longest_common_prefix(char* s,int len){
int sublen=len;
while(--sublen){//子列长度 从大到小进行检验
if(same_string(create_substring(s,0,sublen-1),create_substring(s,len-sublen,len-1))==1){//看左右子列是否相同
return sublen;//如果相同就输出子列长度
}
}
return 0;//否则输出0
}
//输出前缀表
void print_prefix_table(table* t){
for(int i=0;i<t->size;i++){
printf("[%d]%c(%d)\n",i,*(t->data[i]),t->prefix[i]);
}
}
//输出字符串长度
int string_length(char* a){
int i=0;
while(a[i]!='\0'){
i++;//i:a的总字符数
}
return i;
}
//检查两个字符串是否相等,相等输出1
int same_string(char* a,char* b){
int i=0;
while(a[i]==b[i]){
i++;
if(a[i]=='\0'&&b[i]=='\0'){
return 1;
}
}
return 0;
}
int main()
{
char* a="aaaaaaaaaaaaaaaaaab";
char* b="aaab";
printf("%d",KMP(a,b));
return 0;
}