串( string)是由零个或多个字符组成的有限序列,又名叫字符串。
一般记为:S = ′ a1a2...a n ′ ( n > = 0 ) S='a1a2...an' (n>=0)。
其中,S是串名,单引号括起来的字符序列是串的值; an可以是字母、数字或其他字符;串中字符的个数n称为串的长度。
串的基本概念
#串的定义
串是由0个或多个字符组成的有限序列,又叫字符串。
#空串
长度为0的串,不包含任何字符。
#空白串
由一个或多个空白字符组成的串。
#子串
串的任意个连续字符组成的串子序列。子串的位置是以子串在主串中首次出现时的第一个字符在主串中的位置来表示。
#主串
包含子串的串相应的叫主串。
#串的比较
串的比较是通过组成串中的字符之间的编码来进行的,而字符的编码指的是字符在字符集中的序号。通过两串字符的比较,若不等,则返回第一个不相等的字符的数值差。
例: compareTo(str)算法
若当前串 > str ,则返回值 > 0;
当前串 < str ,则返回值 < 0;
当前串 = str ,则返回值 = 0;
串的接口描述
串实际是线性表的一种,它与一般的线性表的不同之处是其每个数据元素的类型一定是字符类型,不能是其它类型。
串的基本操作
StrAssign(&T, chars): 赋值操作。把串T赋值为 chars
Strcopy(&T, S): 复制操作。由串S复制得到串T。
StrEmpty(S): 判空操作。若S为空串,则返回TRUE,否则返回 FALSE
StrCompare(S,T): 比较操作。若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0。
StrEngth(S): 求串长。返回串S的元素个数
Substring(&Sub,S,pos,1en):求子串。用Sub返回串S的第pos个字符起长度为len的子串。
Concat(&T,S1,S2): 串联接。用T返回由S1和S2联接而成的新串。
Index(S,T): 定位操作。若主串S中存在与串T值相同的子串,则返回它在主串S中第一次出现的位置;否则函数值为0
Clearstring(&S): 清空操作。将S清为空串
Destroystring(&S): 销毁串。将串S销毁
顺序串的操作与实现
顺序串的存储结构
#define MAXLINE 255 //用户可用的最大串长
typedef struct{
char ch[ MAXLINE+1 ]; // 下标0的分量闲置不用
int length;//串的当前长度
} SString; //s是一个可容纳255个字符的顺序串。
串的块链储存结构
#define MAXLINE 80
typedef struct Chunk{
char ch[CHUNKSIZE];
struct Chunk *next;
} Chunk;
typedef struct{
Chunk *head,*tail;//串的头指针和尾指针
int curlen;//串的当前长度
}LString;//字符串的块链结构
BF算法(暴力算法)
BF算法的思想是:
1.将目标串S的第一个字符与模式串T的第一个字符进行匹配;
2.若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符;
3.依次比较下去,直到得出最后的匹配结果;
算法效率:
若主串长度位m,子串长度位n,则:
最好情况平均时间复杂度:O(m+n)
最坏情况平均时间复杂度:O(m*n)
从头找:
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;//匹配不成功
}
kmp算法
在上面的简单匹配中,每趟匹配失败都是模式后移一位再从头开始比较。而某趟已匹配相等的字符序列是模式的某个前缀,这种频繁的重复比较相当于模式串在不断地进行自我比较,这就是其低效率的根源。
因此,可以从分析模式本身的结构着手,如果已匹配相等的前缀序列中有某个后缀正好是模式的前缀,那么就可以将模式向后滑动到与这些相等字符对齐的位置,主串i指针无须回溯,并继续从该位置开始进行比较。而模式向后滑动位数的计算仅与模式本身的结构有关,与主串无关。
KMP算法的特点就是:仅仅后移模式串,比较指针不回溯。
注:
若主串长度位m,子串长度位n,则:
最坏情况平均时间复杂度:O(m+n)
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]; //i不变,j后退
}
if (j>T.length) return i-T.length; //匹配成功
else return 0; //返回不匹配标志
}
void get_next(SString T,int &next[] ) {
//next函数值存入数组next
i=1;
next[1]=0;
j=0;
while(i<T.length ) {
if ( j==0||T.ch[i]==T.ch[j] ) {
++i;
++j;
next[i]=j;
}
else j=next[j];
}
}
next函数的改进:
/*nextval数组求法*/
void get_nextval(SString T, int &nextval[])
{
i=1;nextval[1]=0;j=0;
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){
++i; ++j;
if(T.ch[i]!=T.ch[j])
nextval[i]=j;
else
nextval[i]=nextval[j];
}
else j=nextval[j];
}
}