一.简单模式匹配。
主串str1与模式串str2,如果str1[ i ]==str2[ j ],那么j++,i++,匹配两串的下一位,如果不相符,则str1回到起始点的下一位,即 i = i - j +2,str2回到第一位,即 j =1;比较完成若 j = str2.length,则表示 j 已经比较到最后一位的下一位,也就是比较成功。
#include <stdio.h>
#define maxsize 100
#define true 1
#define false 0
typedef struct{
char ch[maxsize];
int length;
}Str;
void InitString(Str& str){
str.ch[0]='\0';
str.length=0;
}
void strAssign(Str& str,char chars[]){
int i=0;
while(chars[i]!='\0'){
str.ch[i+1]=chars[i++];
}
str.length=i+1;
}
void printString(Str str){
if(str.length==0)
printf("该串为空串!");
else{
for(int i=1;i<str.length;i++)
printf("%c ",str.ch[i]);
printf("\n");
}
}
int index(Str str,Str substr){
int i=1,j=1;
//串长包含了数组为'\0'的值,使用有效字符数为length-1;
//如str1的length为5,则有4个有效字符,当i<length即i<=4时str.ch[i]是有效字符
//如果i<=length,那么i<=5,但str.ch[5]超出范围;
while(i<str.length && j<substr.length){
if(str.ch[i]==substr.ch[j])
{ ++i;
++j; }
else{
j=1;
i=i-j+2;
}
}
//如果是j>substr.length,也就是j大于整个子串的长度(包含了'\0');上述条件j最多加到length就退出,
//不满足条件,所以j最大就是子串的长度,使用当j等于子串长也就是大于子串长减一时匹配成功
//如果i=5,j=3,那么返回的位置是3,因为i,j都是匹配位的下一位,当i=4,j=2时字符相等 ,子串有效字符长度为i-j
//也就是i=3时开始匹配,所以子串第一次出现的位置是3,返回3,也就是i-j+1;
if(j>substr.length-1)
return i-substr.length+1;
else
return 0;
}
int main(){
Str str1,str2;
char char1[]="abcdefj";
char char2[]="cde";
InitString(str1);
InitString(str2);
strAssign(str1,char1);
strAssign(str2,char2);
printString(str1);
printString(str2);
printf("子串在主串第一次出现的起始位置为%d",index(str1,str2));
}
二.KMP算法
核心思想是的到子串的next数组,也就是子串的最长相等前后缀的个数,当主串与子串不匹配时,子串回溯到当前值的next[ j ]值位置开始与i(不变)重新匹配,j=next[ j ],小于 j 的就是最长相等前后缀的前缀部分,重新开始匹配后该前缀部分与最长相等前后缀的后缀部分重合,也就是说这一部分不需要多余的匹配,如果重新匹配后的str.ch[i]!=str.ch[j],那么要回溯到j=next[j],也就是j=next[ next[ j ] ]的位置重新开始与i匹配,要是最长前后前后缀值为1,则回到子串开头与第i位的主串重新匹配。
输出如下 :
代码如下:
#include <stdio.h>
#define maxsize 100
#define true 1
#define false 0
typedef struct{
char ch[maxsize];
int length;
}Str;
void InitString(Str& str){
str.ch[0]='\0';
str.length=0;
}
void strAssign(Str& str,char chars[]){
int i=0;
while(chars[i]!='\0'){
str.ch[i+1]=chars[i++];
}
str.length=i+1;
}
void printString(Str str){
if(str.length==0)
printf("该串为空串!");
else{
for(int i=1;i<str.length;i++)
printf("%c ",str.ch[i]);
printf("\n");
}
}
void get_next(Str substr,int next[]){
int i=1,j=0;
next[1]=0; //第一位的next值为0;
/*如:abcdabd;
当j=0是,满足if,得到next[2]=1;
第二轮循环,a!=b,也就是str.ch[1]!=str.ch[2],则j=next[j]=next[1]=0;
第三轮,j=0,i=2,得到next[3]=1;
第四轮,j=1,i=3,但a!=c,所以j=next[1]=0;
第五轮,j=0;得到j=1,i=4,next[4]=1;
第六轮,a!=d,所以j=0;
第七轮,j=0;j=1,i=5,next[5]=1;
第八轮,a=a,得j=2,i=6,next[6]=2;
第九轮,b=b,得j=3,i=7,next[7]=3;
*/
while(i<substr.length){
if(j==0||substr.ch[i]==substr.ch[j]){
i++;
j++;
next[i]=j;
}
else{
j=next[j];
}
}
for(int i=1;i<substr.length;i++){
printf("next[%d]=%d ",i,next[i]);
}
printf("\n");
}
int index(Str str,Str substr,int next[]){
int i=1,j=1;
while(i<str.length && j<substr.length){
//j==0,表示子串第一个字符与主串不匹配,此时要从i的下一位i+1开始与j=1匹配;
if(j==0||str.ch[i]==substr.ch[j])
{ ++i;
++j; }
else{
j=next[j];
}
}
if(j>=substr.length)
return i-substr.length+1;
else
return 0;
}
int main(){
Str str1,str2;
char char1[]="abcdabcdabd";
char char2[]="abcdabd";
int next[10];
InitString(str1);
InitString(str2);
strAssign(str1,char1);
strAssign(str2,char2);
printString(str1);
printString(str2);
get_next(str2,next);
printf("子串在主串第一次出现的起始位置为%d",index(str1,str2,next));
}