一、串的基本定义
串是内容受限的线性表(数据元素只能是字符),是由零个或多个任意字符组成的有限
子串:一个串中任意个连续字符组成的子序列(含空串),另外真子串是指不包含自身的所有子串
主串:包含子串的串
字符位置:字符在序列中的序号
子串位置:子串第一个字符在主串中的位置
空格串:由一个或多个空格组成的串
串相等:两个串长度相等并且各个对应位置的字符都相同
二、串的存储结构
串中元素的逻辑关系与线性表的相同,因此可以采用与线性表相同的存储结构,顺序存储结构的叫顺序串,链式存储结构的叫链串。
顺序存储:元素的逻辑关系直接映射到物理位置,用物理位置的先后表示逻辑关系的先后,最简单的就是用数组实现。
#define MAXLEN 255
typedef struct{
char ch[MAXLEN+1]; //下标为0的位置不用
int length; //当前串的长度
}SString;
链式存储操作方便但是存储密度较低,可将多个字符放在一个结点中克服以上缺点。
#define CHUNKSIZE 80 //块的大小
typedef struct Chunk{
char ch[CHUNKSIZE];
struct Chunk *next;
}Chunk;
typedef struct{
Chunk *head,*tail; //串的头指针和尾指针
int curlen; //串的长度
}LString;
三、串的运算
1.模式匹配算法
目的:确定主串中子串第一次出现的位置(定位)
应用:搜索引擎、语言翻译、拼写检查、数据压缩
种类:BF(暴力破解)、KMP
BF:从主串每一个字符开始依次与子串的字符进行匹配。假设主串S=”aaaaab“,子串T=”aaab“。从S的第一个字符与T的第一个字符开始匹配,一致的话匹配下一个字符,直到第四个字符,不一致,重新从S的第二个字符与T的第一个字符开始匹配,以此类推。
int Index_BF(SString S,SString T){
int i=1,j=1;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]) {i++;j++;}
else {i=i-j+2;j=1;}
}
if(j>=T.length) return i-T.length;
else return 0;
}
//从主串指定位置开始查找的话
int Index_BF(SString S,SString T,int pos){
int i=pos,j=1;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]) {i++;j++;}
else {i=i-j+2;j=1;}
}
if(j>=T.length) return i-T.length;
else return 0;
}
BF算法时间复杂度:设主串长度为n,子串长度为m。最好的情况是比较m次即O(m);最坏情况是m(n-m)+m次即O(mn);平均的话是O(nm)=O(mn)。
KMP算法:利用已经部分匹配的结果而加快子串的滑动速度,且主串的指针不必回溯,可提速到O(n+m)。该算法需要子串存在公共前后缀(前缀从子串第一个字符开始往后扩大,后缀从不匹配位置旁边的字符开始往前扩大,但这个字符是后缀的尾而不是头),以便不匹配时滑动子串,前缀能一下移到后缀的位置。如果有多个前后缀,选最长的,然后移完后前缀有n个字符就从子串第n+1个字符开始比较。
用一个next数组记录下j值(j的含义是当主串某个字符不匹配时,下一个要从子串第j个字符开始匹配)。
定义neweixt[j]函数,当没有公共前后缀时=1,当j为1时=0,当有公共前后缀时=前缀长度+1
void get_next(SString T,int &next[]){
i=1;next[1]=0;j=0; //i指向后缀末尾位置,j指向前缀末尾位置和i之前(包括i,因为前后
缀可能部分重合)的子串最长公共前后缀的长度
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){ //前后缀相等j往后移比较下一位,i也后移
i++;j++;
next[i]=j;
}
else j=next[j]; //前后缀不相等的情况j回退
}
}
int Index_KMP(SString S,SString T,int pos){
i=pos,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;
}