第四章-串
□4.2 串的表示和实现
// PROJECT04-02.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "c1.h"
/*----------------------------------------
串的堆分配储存表示
------------------------------------------*/
typedef struct{
char* ch; /*若是非空串,则按串长分配存储区,否则ch为NULL*/
int length; /*串长度*/
}HString;
Status StrAssign(HString *T,char *chars)
{ /* 生成一个其值等于串常量chars的串T */
char *c = NULL;
int i = 0;
int count = 0;
if (T->ch){
free(T->ch);/*释放T原有的空间*/
}
count = strlen(chars); /* 求chars的长度i */
if (count == 0){
T->ch = NULL;
T->length = 0;
}
else{
if (!(T->ch = (char*)malloc(count + sizeof(char)))){
exit(OVERFLOW);
}
T->length = count;
}
for(i = 0; i < T->length; i++){
*(T->ch + i) = *(chars + i);
}
return OK;
}
Status StrCopy(HString *T,HString S)
{ /* 初始条件:串S存在。操作结果: 由串S复制得串T */
int i;
if((*T).ch){
free((*T).ch); /* 释放T原有空间 */
}
(*T).ch = (char*)malloc(S.length * sizeof(char)); /* 分配串空间 */
(*T).length = S.length;
for(i=0; i < S.length; i++){
*(T->ch + i) = *(S.ch + i);
}
return OK;
}
Status StrEmpty(HString S)
{ /* 初始条件: 串S存在。操作结果: 若S为空串,则返回TRUE,否则返回FALSE */
if(S.length == 0 && S.ch == NULL){
return TRUE;
}
else{
return FALSE;
}
}
int StrLength(HString S)
{
return S.length;
}
int StrCompare(HString S,HString T)
{ /* 若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0 */
int i;
for (i = 0;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.length;
}
Status ClearString(HString *S)
{ /*将S清为空串*/
if ((*S).ch){
free((*S).ch);
(*S).ch = NULL;
}
(*S).length = 0;
return OK;
}
Status Concat(HString *T, HString S1, HString S2)
{ /*用T返回由S1和S2联结而成的新串*/
int i;
if((*T).ch){
free((*T).ch); /* 释放T原有空间 */
}
if(!((*T).ch = (char*)malloc((S1.length + S2.length) * sizeof(char))) ){
exit(OVERFlOW);
}
(*T).length = S1.length + S2.length;
for(i = 0;i < S1.length; i++ ){
(*T).ch[i] = S1.ch[i];
}
for(i = 0;i < S2.length; i++ ){
(*T).ch[i + S1.length] = S2.ch[i];
}
return OK;
}
Status SubString(HString *Sub, HString S,int pos,int len)
{ /* 用Sub返回串S的第pos个字符起长度为len的子串。 */
/* 其中,1≤pos≤StrLength(S)且0≤len≤StrLength(S)-pos+1 */
int i;
if (pos < 1 || pos > S.length || len < 0 || len > S.length - pos + 1 ){
return ERROR;
}
if (Sub->ch){ /* 释放旧空间 */
free(Sub->ch);
Sub->ch = NULL;
}
if(len == 0){ /* 空子串 */
Sub->ch = NULL;
Sub->length = 0;
}
else{ /* 完整子串 */
Sub->ch = (char*)malloc(len * sizeof(char));
for (i = 0; i < len; i++){
Sub->ch[i] = S.ch[pos + i - 1];
}
Sub->length = len;
}
return OK;
}
void InitString(HString *T)
{ /* 初始化(产生空串)字符串T。另加 */
(*T).length = 0;
(*T).ch = NULL;
}
int Index(HString S, HString T, int pos) /* 算法4.1 */
{ /* T为非空串。若主串S中第pos个字符之后存在与T相等的子串, */
/* 则返回第一个这样的子串在S中的位置,否则返回0 */
int n, m, i;
HString sub;
InitString(&sub);
if(pos > 0){
n = StrLength(S);
m = StrLength(T);
i = pos;
while(i <= n - m + 1){
SubString(&sub, S, i, m);
if(StrCompare(sub, T) != 0){
++i;
}
else{
return i;
}
}
}
return 0;
}
Status StrDelete(HString *S,int pos,int len)
{ /* 从串S中删除第pos个字符起长度为len的子串 */
int i;
if((*S).length < pos + len -1){
exit(ERROR);
}
for(i = pos-1; i <= (*S).length - len; i++){
(*S).ch[i] = (*S).ch[i + len];
}
(*S).length -= len;
(*S).ch = (char*)realloc((*S).ch,(*S).length * sizeof(char));
return OK;
}
Status StrInsert(HString *S,int pos,HString T) /* 算法4.4 */
{ /* 1≤pos≤StrLength(S)+1。在串S的第pos个字符之前插入串T */
int i;
if (pos >= 1 && pos >= StrLength(*S)+1){
exit(ERROR);
}
(*S).ch = (char*)realloc((*S).ch,((*S).length + T.length) * sizeof(char));
if(!(*S).ch){
exit(OVERFLOW);
}
/* 为插入T而腾出位置 */
for(i = (*S).length - 1 ;i >= pos-1; i--){
(*S).ch[i+T.length] = (*S).ch[i];
}
for(i=0; i< T.length; i++){
(*S).ch[i+pos-1] = T.ch[i];/* 插入T */
}
(*S).length += T.length;
return OK;
}
Status Replace(HString *S,HString T,HString V)
{ /* 初始条件: 串S,T和V存在,T是非空串(此函数与串的存储结构无关) */
/* 操作结果: 用V替换主串S中出现的所有与T相等的不重叠的子串 */
int i=1; /* 从串S的第一个字符起查找串T */
if(StrEmpty(T)){ /* T是空串 */
return ERROR;
}
do{
i = Index(*S,T,i);
if(i){ /* 串S中存在串T */
StrDelete(S,i,T.length); /* 删除该串T */
StrInsert(S,i,V); /* 在原串T的位置插入串V */
i += StrLength(V); /* 在插入的串V后面继续查找串T */
}
}while(i);
return OK;
}
void DestroyString()
{ /* 堆分配类型的字符串无法销毁 */
}
void StrPrint(HString T)
{ /* 输出T字符串。另加 */
int i;
for(i = 0; i < T.length; i++){
printf("%c", T.ch[i]);
}
printf("\n");
}
int _tmain(int argc, _TCHAR* argv[])
{
int i;
char c,*p="God bye!",*q="God luck!";
HString t,s,r;
InitString(&t); /* HString类型必需初始化 */
InitString(&s);
InitString(&r);
StrAssign(&t,p);
printf("串t为: ");
StrPrint(t);
printf("串长为%d 串空否?%d(1:空 0:否)\n",StrLength(t),StrEmpty(t));
StrAssign(&s,q);
printf("串s为: ");
StrPrint(s);
i=StrCompare(s,t);
if(i<0)
c='<';
else if(i==0)
c='=';
else
c='>';
printf("串s%c串t\n",c);
Concat(&r,t,s);
printf("串t联接串s产生的串r为: ");
StrPrint(r);
StrAssign(&s,"oo");
printf("串s为: ");
StrPrint(s);
StrAssign(&t,"o");
printf("串t为: ");
StrPrint(t);
Replace(&r,t,s);
printf("把串r中和串t相同的子串用串s代替后,串r为:\n");
StrPrint(r);
ClearString(&s);
printf("串s清空后,串长为%d 空否?%d(1:空 0:否)\n",StrLength(s),StrEmpty(s));
SubString(&s,r,6,4);
printf("串s为从串r的第6个字符起的4个字符,长度为%d 串s为: ",s.length);
StrPrint(s);
StrCopy(&t,r);
printf("复制串t为串r,串t为: ");
StrPrint(t);
StrInsert(&t,6,s);
printf("在串t的第6个字符前插入串s后,串t为: ");
StrPrint(t);
StrDelete(&t,1,5);
printf("从串t的第1个字符起删除5个字符后,串t为: ");
StrPrint(t);
printf("%d是从串t的第1个字符起,和串s相同的第1个子串的位置\n",Index(t,s,1));
printf("%d是从串t的第2个字符起,和串s相同的第1个子串的位置\n",Index(t,s,2));
return 0;
}
□4.3 串的模式匹配算法
求子串位置的定位函数Index(S,T,pos)
子串的定位操作通常称作串的模式匹配,是各种串处理系统中最重要的操作之一。在Index这个函数中,分别利用计数指针i和j指示主串S和模式串T中当前正待比较的字符位置。算法的基本思想是:从主串S的第pos各字符起和模式的第一个字符比较之,若相等则继续逐个比较后续字符;否则从主串的下一个字符起再重新和模式的字符比较之。
□4.3.2 模式匹配的一种改进算法
克努特-莫里斯-普拉特操作(KMP算法)
其改进在于每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的"部分匹配"的结果将模式向右"滑动"尽可能远的一段距离后,继续进行比较。
KMP算法仅当模式与主串之间存在许多"部分匹配"的情况下才显得比算法4.5快得多。但是KMP算法的最大特点就是指示主串的指针不需回溯。
next[j]表明当模式中第j个字符与主串中相应字符失配时,在模式中需要重新和主串中该字符进行比较的字符的位置。匹配过程如下:假设以指针i和j分别指示主串和模式中正待比较的字符,令i的初值为pos,j的初值为1。若在匹配过程中si = pj,则i和j分别增1,否则i不变,而j退到next[j]的位置再比较,若相等,则指针各自增1,否则j再退到下一个next值的位置,依次类推,直至下列两种可能:一种是j退到某个next值时比较相等,则指针各自增1,继续进行匹配;另一种是j退到值为0,则此时需将模式继续向右滑动一个位置。即从主串的下一个字符Si+1起和模式重新开始匹配。虽然该算法的时间复杂度是O(n*m),但在一般情况下,其实际的执行时间近似于O(n+m),因此至今仍被采用。KMP算法仅当模式与主串之间存在许多"部分匹配"的情况下才显得快得多。KMP算法的最大特点是指示主串的指针不需回溯,整个匹配过程中,对主串仅需从头至尾扫描一遍,这对处理从外设输入的庞大文件很有效,可以边读入边匹配,而无需回头重读。