串
串的定义
串(string)即字符串,是由零个或多个字符组成的有限序列。一般记作:
s = “ a 1 a 2 . . . a n ” ( n ≥ 0 ) s=“a_1 \ a_2 \ ...\ a_n ” \ (n \geq 0) s=“a1 a2 ... an” (n≥0)
串中字符的数目n称为串的长度。零个字符的串称为空串,其长度为0。串中任意个连续的字符组成的字符组成的子序列称为该串的子串。字串在主串的位置则以字串的第一个字符正在主串中的位置表示。
由1个或多个空格“ ”组成的串,称为空格串,(注意和空串区分),其长度为串中空格字符的个数。
串的存储结构
串也有两种基本存储结构:顺序存储和链式存储。
考虑到存储效率和算法的方便性,多采用顺序存储结构。
- 串的顺序存储
//串的定长顺序存储结构 #define MAXLEN 255 //串的最大长度 typedef struct{ char ch[MAXLEN+1]; int length; //串的当前长度 }SString; //串的堆式顺序存储结构 typedef struct{ char *ch; //非空串按串长分配 否则为NULL int length; //串的当前长度 }HString;
- 串的链式存储
#define CHUNKSIZE 80 //块大小 typedef struct Chunk{ char ch[CHUNKSIZE]; struct Chunk *next; }Chunk; typedef struct{ Chunk *head,*tail; //串的头和尾 int length; //串的当前长度 }LString;
串的模式匹配算法
字串的定位运算称为串的模式匹配或者串匹配。串的模式匹配设有两个字符串S和T,设S为主串,也称正文串;设T为字串,也称为模式。
著名的模式匹配有BF算法和KMP算法。
- BF算法
最简单直观,但是算法的时间复杂度高。
算法描述: 模式匹配从主串中查找的起始位置为pos。 1.分别利用计数指针i和j指示主串S和模式T中当前待比较的字符位置,i初始值为pos,j初值为1。 //字符串从1开始 2.如果两个串均未到达串尾,即i和j均小于等于S和T的长度时,则循环执行以下操作: ·S[i].ch和T[j].ch比较,若相等,则i和j分别指示串中下个位置,继续比较后续的字符。 ·若不等,指针后退重新开始匹配,从主串的下一个字符(i=i-j+2)起再重新和模式的第一个字符(j=1)比较。 ·如果j>T.length,说明模式T中的每个字符依次和主串S中的一个连续的字符序列相等,则匹配成功。返回和模式T中第一个字符相等的字符在主串S中的序列号(i-T.length);否则称匹配失败,返回0。
#include<iostream> #include<cstring> #include<cstdio> using namespace std; //串的定长顺序存储结构 #define MAXLEN 255 //串的最大长度 typedef struct{ char ch[MAXLEN+1]; int length; //串的当前长度 }SString; int Index_BF(SString S,SString T,int pos){ //返回模式T在主串S中第pos个位置开始第一次出现的位置,不存在,返回0 //其中T非空 printf("%s\n",S.ch); printf("%s\n",T.ch); int i=pos,j=0; //初始化 这里是从0开始的 //两个串均为比较到串尾 while(i<S.length&&j<T.length){ if(S.ch[i]==T.ch[j]){ ++i; ++j; }else{ i=i-j+1; //指针后退重新开始匹配 j=0; } } if(j>=T.length) return i-T.length; //匹配成功 else return 0; //匹配失败 } int main(){ SString S,T; strncpy(S.ch,"ababcabcacbab",sizeof(S.ch)); strncpy(T.ch,"abcac",sizeof(T.ch)); S.length=13; T.length=5; int ans=Index_BF(S,T,0); printf("%d\n",ans); return 0; }
- KMP算法
算法复杂度O(m+n).相比于BF算法的改进:每当一趟匹配过程中出现字符比较不等时,不需要回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离,继续进行比较。从当前位置j滑动到的位置就是next[j]中存的数。所以重中之重就是求next数组。并且next数组只和模式串有关。
为了便于记忆,这里不从理论讲解,只将一个简单的例子,并给出具体的实现代码。通常字符数组从0开始计数。
假设模式串为abcca
next[j]通常是看位置j前面,即(0~j-1)的字符串的前缀后缀的匹配情况。规定next[0]=-1。
next[1]看第0个字符串的前后缀的匹配情况(前缀和后缀补不包含本身。)第0个子符串匹配为0。所以next[1]=0。
next[2]看0~1字符串即ab,前后缀匹配长度为0,所以next[2]=0.
同理,next[3]=0 ,next[4]=1。
#include<iostream> #include<cstring> using namespace std; //串的定长顺序存储结构 #define MAXLEN 255 //串的最大长度 typedef struct{ char ch[MAXLEN]; int length; //串的当前长度 }SString; //这个数组中村存放的是匹配字符串的next值 int Next[254]; //这个next数组是KMP的重点 void GetNext(SString T,int next[]){ //求模式串T的next值并存入到数组next中 int len=T.length; //串长 int i=0,j=-1; next[0]=-1; while(i<len-1){ if(j==-1||T.ch[i]==T.ch[j]){ ++i; ++j; next[i]=j; }else{ j=next[j]; } } } int Index_KMP(SString S,SString T,int pos){ //利用模式串T的next函数求T在主串S中第pos个字符之后的位置 //T非空 int i=pos-1,j=0; while(i<S.length&&j<S.length){ if(S.ch[i]==T.ch[j]){ ++i; ++j; }else{ j=Next[j]; } } if(j>=T.length) return i-T.length; //匹配成功 else return 0; //匹配失败 } int main(){ SString S,T; //S主串 T模式串 strncpy(S.ch,"ababcabcacbab",sizeof(S.ch)); strncpy(T.ch,"abcac",sizeof(T.ch)); S.length=13; T.length=5; GetNext(T,Next); int ans=Index_KMP(S,T,1); printf("%d\n",ans); return 0; }