1、串
1.1、串的定长顺序存储
#define MAXLEN 255 typedef struct{ char ch[MAXLEN]; int length; }SString;//静态数组实现(定长顺序存储)
方案二的缺点有char只有一个字节的大小只能存储0-255
1.2、串的链式存储
typedef struct{ char *ch; int length; }HString;//动态数组实现(堆分配存储) HString S; S.ch = (char *)malloc(MAXLEN * sizeof(char));//用完要手动free S.length = 0;
1.3、串的基本操作
1.3.1 SubString(&Sub,S,pos,len)求子串
作用:用Sub返回串S的第pos个字符起长度len的子串。
bool SubString(&Sub,S,pos,len) { //子串范围越界 if(pos+len-1 > S.length) retrun false; for(int i = pos; i<pos+len; i++) Sub.ch[i-pos+1] = S.ch[i]; Sub.length = len; return true; }
1.3.2 StrCompare(S,T):比较操作。
作用:若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。
int StrCompare(S,T){ for(int i = 1; i<=S.length && i<T.length;i++){ if(S.ch[i]!=T.ch[i]) return S.ch[i]-T.ch[i]; } //扫描过的所有字符都相同,则长度长的串更长 return S.length-T.lengh; }
1.3.3 Index(S,T):定位操作。
作用:若主串s中存在与串T值相同的子串,则返回它在主串S中第一次出现的位 置;否则函数值为0。
Int Index(SString S,SString T){ int i =1;n = StrLength(S),m =StrLength(T); SString sub;//用于暂存子串 while(i<=n-m+1) { SubString(sub,S,i,m); if(StrCompare(sub,T)!=0) ++i; else return i; //返回子串在主串中的位置 } return 0; }
2、串的模式匹配
2.1 朴素模式匹配算法
int Index(SSring S,SString T){ int k=1; int i=k, j=1; while(i<=S.length && j<=T.length){ if(S.ch[i]==T.ch[j]){ ++i; ++j;//继续比较后继字符 }else{ k++;//检查下一个子串 i= k; j=1; } } if(j>T.length) return k; else return 0; }
若模式串长度为m,主串长度为n,则
匹配成功的最好时间复杂度:O(m)
匹配失败的最好时间负责度:O(n-m+1)=O(n-m)≈O(n)
朴素模式匹配算法的缺点:当某些子串与模式串能部分匹配时,主串的扫描指针i经常回溯,导致时间开销增加
2.2 KMP算法
改进思路:主串指针不回溯,只有模式串指针回溯
Int Index_KMP(SString S,SString T,int next[]) { int i = 1,j=1; while(i<=S.length&&j<=T.length){ if(j==0||S.ch[i]==T.ch[j]){ ++i; ++j; //继续比较后续的字符 } else j = next[j]; //模式串向右移动 } if(j>T.length) return i-T.length; //匹配成功 else return 0; }
举例1
2.2.1 求模式串的next数组
next数组:当模式串的第j个字符匹配失败时,令模式串跳到next[j]再继续匹配
串的前缀:包含第一个字符,且不包含最后一个字符的子串
串的后缀:包含最后一个字符,且不包含第一个字符的子串
当第j个字符匹配失败,由前1~j-1个字符组成的串记为S,则:
next[i]=S的最长相等前后缀长度+1
特别地:next[1]=0、next[2]=1
举例1:
举例2:
//KMP算法 Int Index_KMP(SString S,SString T,int next[]) { int i = 1,j=1; int next[T.length+1]//O(m) get_next(T,next) while(i<=S.length&&j<=T.length){//O(n) if(j==0||S.ch[i]==T.ch[j]){ ++i; ++j; //继续比较后续的字符 } else j = next[j]; //模式串向右移动 } if(j>T.length) return i-T.length; //匹配成功 else return 0; }
KMP算法平均时间复杂度:O(m+n)
2.2.2 KMP算法的进一步优化
nextval数组的求法:
先算出next数组 先令nextval[1]=0 for(int j = 2;j<=T.length;j++) { if(T.ch[next[j]]==T.ch[j]) nextval[j]=nextval[next[j]]; else nextval[j]=next[j]; }
KMP算法优化:当子串和模式串不匹配时j=nextval[j];