290 likes | 459 Vues
第五章 串和数组. 5.1 串的定义和操作 5.2 串的表示和实现 5.3 字符串应用 5.4 字符串匹配算法 5.5 数组 5.6 数组的压缩. 5.1 串的定义和操作. 串定义 字符串,由零个或多个字符组成的有限序列。 S =“ a 0 a 1 .....a n-1 ” 串的长度: n 空串: n=0 , Null String 子串与主串,子串的位置(从 0 开始) 串的比较:最大相等前缀子序列. 串的基本操作 1)StrAssign(&T,chars) strcpy
E N D
第五章 串和数组 5.1 串的定义和操作 5.2 串的表示和实现 5.3 字符串应用 5.4 字符串匹配算法 5.5 数组 5.6 数组的压缩
5.1串的定义和操作 • 串定义 • 字符串,由零个或多个字符组成的有限序列。S=“a0a1.....an-1” • 串的长度:n • 空串:n=0,Null String • 子串与主串,子串的位置(从0开始) • 串的比较:最大相等前缀子序列
串的基本操作 1)StrAssign(&T,chars) strcpy 2) StrCopy(&T,S) strcpy 3) StrEmpty(S) strlen(S)==0 4) StrCompare(S,T) strcmp 5) StrLength(S) strlen 6) Concat(&T,S1,S2) strcat 7) Substring(&Sub,S,Pos,len) 0<=pos<=Strlength(S)-1 0<=len<=Strlength(S)-pos strncpy 8) Index(S,T,pos) 0<=pos<=Strlength(S)-1 strstr 9) Replace(&S,T,V) 10) StrInsert(&S,pos,T) 0<=pos<=Strlength(S) 11) StrDelete(&S,pos,len) 0<=pos<=StrLength(S)-len 12) DestroyString(&S) • 最小操作子集 StrAssign、StrCompare、StrLength、Concat,Substring
5.2串的表示和实现 5.2.1定长顺序存储表示 • 两种表示方法 • 下标为0的数组存放长度 (pascal) typedef unsigned char SString[MAXSTLEN+1] ; • 在串值后面加‘\0’结束 (C语言) char str[10]; 算法5.1 void Concat_Sq1(char T[],char S1[], char S2[]) 【注意】 T[]必须足够长度,否则会溢出 算法5.2 void Concat_Sq2(SString T[],SString S1[], SString S2[])
5.2.2堆分配存储表示 • 串变量的存储空间是在程序执行过程中动态分配的,程序中出现的所有串变量可用的存储空间是一个共享空间,称为“堆”。 算法5.3求字符串长度 void StrLength(char *S) 算法5.4字符串比较 void StrCompare (char *S, char *T) 算法5.5求字符串字串 void SubString (char *Sub, char *S,int pos,int len) 用Sub返回S中pos位置开始长度为len的子串
5.2.3块链存储表示 • 用链表来存储串。存在节点大小问题 Const CHUNKSIZE =80; typedef struct Chunk{ char ch[CHUNKSIZE]; struct Chunk * next; }Chunk; typedef struct { Chunk *head, *tail; int curlen; }Lstring;
Relplacestring实现 char *replacestring(char *s,char *oldstr, char* newstr){ char *p1=s,*p2,tmpfield[MAX_LEN]; int nlen=0,ilen; while((p2=strstr(p1,oldstr))!=NULL){ ilen=p2-p1; memcpy(tmpfield+nlen,p1,ilen); memcpy(tmpfield+nlen+ilen,newstr,strlen(newstr)); nlen+=ilen+strlen(newstr); p1=p2+strlen(oldstr); } //while if(p1!=s) { memcpy(tmpfield+nlen,p1,strlen(p1)); nlen+=strlen(p1); memmove(s,tmpfield,nlen); s[nlen]=‘\0’; } //if } // replacestring
5.3字符串应用--正文编辑 • 正文编辑通过页表和行表来维护正文串。 • 页表中每一项给出页号和该页的起始行号 • 行表中每一项给出行号、起始地址和该行子串的长度 • 当插入和删除字符时需要修改行表中该行的长度,若超出预先分配的存储空间,还需要重新分配 • 当插入和删除一行时对行表也进行插入和删除;若删除的是一页的首行,则要求修改页表中相应的起始行号 • 当删除一页时仅仅需要修改页表
5.4正文匹配模式 • 算法5.6int find_BF (char *S,char *P,int start) { i = start; j = 0; while ( S[i] != '\0' && P[j] != '\0' ) if ( S[i] == P[j] ) {i++;j ++;} else { i =i-j+1; j = 0; } if ( P[j] == '\0' ) return (i-j); else return -1; } • 例:S=“ababcabcacbab” T=“abcac” 返回值=5 • 算法复杂度:O(m×n)与首字母在S中的出现概率有关
i=1 i=6 a b c a b c a b c d a b c a b c a b c d a b c a b c d a b c a b c d j=0 j=6 S[1]=P[1] !=P[0] P[0..2]= P[3..5] (b) (a) i=6 i=3 a b c a b c a b c d a b c a b c a b c d a b c a b c d a b c a b c d j=3 j=0 S[3..5]=P[3..5] =P[0..2] (d) (c)
S i j P 失配S[i]!=P[j] 匹配 Pj-kPj-k+1…Pj-1= Si-k Si-k+1…Si-1 k 匹配 P0P1…Pk-1= Si-k Si-k+1…Si-1 P0P1…Pk-1=Pj-kPj-k+1…Pj-1 next[j]=k
匹配模式的改进算法(KMP算法) -1 j=0 next[j]= max{k|0<=k<j 且 ‘p0…pk-1’=‘pj-k…pj-1’} next[j]表示当模式中第j个字符与主串失配时,在模式 中需要重新和主串该字符进行比较的字符位置
基于KMP的模式匹配算法 int find_KMP(char *S,char *P,int start){ i=start; j=0; while(i<strlen(S) && j<(int)strlen(P)){ if(j==-1 || S[i]==P[j]){++i;++j} else j=next[j]; } if(j=strlen(P)) return (i-j); else return -1; }
Next数组如何获取 P j Next[j]=k P0P1…Pk-1= Pi-k Pi-k+1…Pi-1 k P P[j]==P[k] Next[j+1]=k+1 P[j]!=P[k] P[j]==P[k’]? k’=next[k] k’ P P[j]==P[k’] Next[j+1]=k’+1
Next数组如何获取 根据定义 next[0]=-1;next[1]=0; 假设next[j]=k; 考虑next[j+1] 由next[j]=k,P0 P1 …Pk-1= Pj-k Pj-k+1 …Pj-1 下面考虑Pk和Pj,若Pk=Pj,则 P0 P1 …Pk-1 Pk = Pj-k Pj-k+1 …Pj-1 Pj 即next[j+1]=k+1; 若Pk!=Pj,希望找到k’<k 使得 P0 P1 …Pk’-1 Pk’ = Pj-k’ Pj-k’+1 …Pj-1 Pj 则有next[j+1]=k’+1, k’即为next[k].
获得next数组的算法 void get_next(char* P,int next[]){ j=0; k=-1;next[0]=-1; while(j<strlen(P)-1){ if(k==-1|| P[j]==P[k]){j++; k++;next[j]=k;} else k=next[k]; } }
改进后获得next数组算法 void get_next1(char* P,int next1[]){ j=0; k=-1;next1[0]=-1; while(j<strlen(P)-1){ if(k==-1|| P[j]==P[k]){ j++; k++; if(P[j]!=P[k])next1[j]=k; else next1[j]=next1[k]; } else k=next[k]; } }
5.5数组 5.5.1数组的定义和操作 • 二维数组定义 • 其数据元素是一维数组的线形表 • N维数组定义 • 其数据元素是N-1维数组的线形表 • 数组的基本操作 initarray(&A,n,bound1,bound2...boundn) Destroyarray(&A) value(A,&e,index1,index2......indexn) assign(&A,e,index1,index2......indexn)
5.5.2数组的顺序表示和实现 • 数组元素的两种存储方式 • 行主序存储 • 列主序存储 • 数组中元素在内存映象中的关系: • 二维数组A[m][n] LOC[i,j]=LOC[0,0]+(i*n+j)*L • 三维数组B[p][m][n] LOC[i,j,k]=LOC[0,0,0]+(i*m*n+j*n+k)*L
5.5.3数组应用 • 例5.1寻找两个串最长的公共子串 • 通常的匹配算法复杂度O(mn2) • 利用二维数组构造串之间的关系来求解 Void diagscan(int mat[][],int m,int n,int i,int j,int &manlen, int &jpos) Void diagmax(int mat[][], int m,int n,int &manlen,int &jpos) 算法5.7 int getmaxsamesubstr(char *string1,char *string2,char *&sub)
特征值 i-j=0 特征值 i-j=-(n-1) Mat[0,0] i-j 为固定值 特征值 i-j=m-1 Mat[m-1,n-1]
5.6数组的压缩 5.6.1特殊形状矩阵的存储表示 • 对称矩阵:A[n][n]存储到B[n(n+1)/2] A[i,j]->B[k] k=(i+1)i/2+j i>=j k=(j+1)j/2+i i<j • 三角矩阵:A[n][n]存储到B[n(n+1)/2] A[i,j]->B[k] k=(i+1)i/2+j i>=j 0 i<j • 带状矩阵A[n][n]存储到B[3n-2] A[i,j]->B[k] k=3i-1+(j-i+2)-1=2i+j |i-j|<=1 0 |i-j|>1 • 随机稀疏矩阵 • 非零元比零元少的多且分布无规律的矩阵。
5.6.2随机稀疏矩阵的存储表示 • 三元组顺序表 const MAXSIZE=1000 typedef struct{ int i,j; ElementType e; }Triple; typedef struct{ Triple data[MAXSIZE] int mu,nu,tu; }TSMatrix;
(b) T.data (a) M.data rpos[0]=0 rpos[col]=pos[col-1]+num[col-1]
矩阵转置 void transpose(ElemType M[][],T[][],int m,int n) 时间复杂度O(n×m) • “按需点菜”的思想 复杂度O(M.nu×M.tu) • “按位就座”的思想 • 建立辅助数组 num[n]rpos[n] void createpos(TSMatrix M) • 快速转置 status Transpose TS(TSMatrix M, TSMatrix &T) 时间复杂度 O(M.nu+M.tu)
逻辑链接的顺序表 • 为了能随机存取三元组表示数组的任意一行非零元素,在建立三元组顺序表存储结构同时建立rpos[n]辅助数组,这种带行链接信息的三元组表称为“逻辑连接的顺序表” typedef struct{ Triple data[MAXSIZE]; int rpos[MAXMN]; int mu,nu,tu; }RLSMatrix;
十字链表 • 矩阵运算增加或减少非零元时,使用链表结构来表示三元组序列。 typedef struct OLNode{ int i,j; ElementType e; struct OLNode *rnext,*cnext; }OLNode,*Olink; typedef struct { Olink *rhead,*chead; int mu,nu,tu; }CrossList;
在十字链表中查找元素 void CrossSearch(CrossList M,ElemType x) { i=0;p=*(M.rhead+i); while(i<M.m){ if(!p){i++;p=*(M.rhead+i);} else{ if(p->e==x) cout<<‘(‘<<p->i<<‘,’<<p->j<<‘)’<<endl; p=p->next; } }//while }