数据结构-串的定长顺序存储
2016-07-03 16:53
381 查看
串的定长顺序存储类似于线性表的顺序存储结构,用一组连续的存储单元存储串值的字符序列。
在串的定长顺序存储结构中,按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区,则可以用定长数组表示:
串的基本操做函数如下:
还有一个函数Index函数。做字符串匹配用,这里拿出来单独讨论
其中这个函数内写了两种方法:第一种调用基本函数的方法,第二种模式匹配算法。
但模式匹配算法还有一个很是经典的算法模式匹配的改进算法-KMP算法;
其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右滑动尽可能远的一段距离后,继续进行比较。
KMP算法的基本思想就是这样,剩下的百度搜索全是我就不再细说,还是按我的习惯直接上代码。
KMP算法时间复杂度O(m)。通常,模式串的长度m比主串的长度n要小的多,因此对整个匹配算法来说,所增加的这点时间是值得的。
但是,虽然模式匹配算法时间复杂度是O(n*m),但是一般情况下,其实际的实行时间近似于O(n+m),因此至今仍被采用。
KMP算法仅当模式与主串之间存在许多“部分匹配”的情况下才显得比模式匹配算法快得多。但是KMP算法的最大特点是指主串的指针不需回溯,整个匹配过程中,对主串仅需从头至尾扫描一遍,这对处理从外设输入的庞大文件很有效,可以边读入边匹配,而无需回溯重读。
但对于next函数前面所写的在某些情况下有一个缺陷,例如在模式aaaab,主串aaabaaaab匹配时,当i=4,j=4时ch[4]!=t.ch[4]。由next[j]的指示还需要进行i=4,j=3;i=4,j=2;i=4,j=1这三次比较。实际上,因为模式中第1,2,3个字符和第4个字符都相等,因此不必要在和主串中第4个字符相比较,而可以将模式一下子滑动4个位置。直接进行i=5,j=1时的字符比较。
改进的next函数如下:此时匹配算法不变;
在串的定长顺序存储结构中,按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区,则可以用定长数组表示:
/*串定长顺序存储表示*/ #define MAXSTRLEN 255 //串在MAXSTRLEN大小 typedef unsigned char SString[MAXSTRLEN + 1]; //所有串的0号单元存储串的长度
串的基本操做函数如下:
/*生成一个其值等于chars的串T*/ Status StrAssign(SString &T, char chars[]) { if(strlen(chars) > MAXSTRLEN) return ERROR; T[0] = strlen(chars); for (int i = 0; i <= T[0]; i++) T[i+1] = chars[i]; return OK; } /*由串S赋值的到串T*/ Status StrCopy(SString &T, SString S) { int i = 1; for (; i <= S[0]; i++) T[i] = S[i]; T[0] = S[0]; return OK; } /*判断串S是否为空,若S为空串侧返回true,否则返回false*/ bool StrEmpty(SString s) { if(s[0] == 0) return true; else return false; } /*若S>T,则返回值大于0;若S=T,则返回值等于0;若S<T则返回值小于0*/ Status StrCompare(SString S, SString T) { for (int i = 1; i <= S[0] && i <= T[0]; ++i) if(S[i] != T[i]) return S[i] - T[i]; return S[0] - T[0]; } /*返回串的长度*/ int StrLength(SString S) { return S[0]; //串的第0个存储放的串的长度 } /*重置串为空串*/ Status ClearString(SString &S) { S[0] = 0; //因为是顺序存储,只要将长度标志设为0即是代表已经清空串 return OK; } /*用T返回由串S1和串S2连接成的新串。若未截断,则返回true,否则返回false*/ bool Concat(SString &T, SString S1, SString S2) { bool uncut = true; if(S1[0] + S2[0] <= MAXSTRLEN) //未截断 { for (int i = 1; i <= S1[0]; i++) T[i] = S1[i]; for (int i = 1; i <= S2[0]; i++) T[i + S1[0]] = S2[i]; T[0] = S1[0] + S2[0]; uncut = true; } else if(S1[0] < MAXSTRLEN) //截断;只取S2的一部分 { int i = 1; for ( ; i <= S1[0]; i++) T[i] = S1[i]; for (i = 1; i <= MAXSTRLEN-S1[0]; i++) T[i + S1[0]] = S2[i]; T[0] = MAXSTRLEN; uncut = false; } else //截断;仅取S1 { for (int i = 0; i <= MAXSTRLEN; i++) T[i] = S1[i]; uncut = false; } return uncut; } /*用sub返回串S的第pos个字符起的长度为len的子串*/ Status SubString(SString &Sub, SString S, int pos, int len) { if(pos < 1 || pos > S[0] || len < 0 || len > S[0]-pos+1) return ERROR; for (int i = 1; i <= len; i++) Sub[i] = S[pos+i-1]; Sub[0] = len; return OK; } /*用串V替换主串中出现的所有*/ Status Replace(SString &S, SString T, SString V) { if(StrEmpty(T)) return ERROR; int i = 1, j = 1; int m = StrLength(T), n = StrLength(V); while(i <= S[0]) { j = Index(S,T,i); StrDelete(S,j,m); StrInsert(S,j,V); i+=n+1; } return OK; } /*在串S的pos位置之前插入串T*/ Status StrInsert(SString &S, int pos, SString T) { if(pos < 1 || pos > S[0]+1) return ERROR; if(S[0]+T[0] <= MAXSTRLEN) //完全插入 { for (int i = S[0]; i >= pos; i--) S[i+T[0]] = S[i]; for (int i = pos; i < pos+T[0]; i++) S[i] = T[i-pos+1]; S[0] = S[0]+T[0]; return OK; } else //不完全插入 { for (int i = MAXSTRLEN; i >= pos; i--) S[i] = S[i-T[0]]; for (int i = pos; i < pos+T[0]; i++) S[i] = T[i-pos+1]; S[0] = MAXSTRLEN; return ERROR; } } /*从串S中删除第pos个字符起的长度为len的子串*/ Status StrDelete(SString &S, int pos, int len) { if(pos < 1 || pos > S[0]-len+1 || len < 0) return ERROR; for (int i = pos+len; i <= S[0]; i++) S[i-len] = S[i]; S[0] -= len; return OK; } /*销毁串S*/ Status DestroyString(SString &S) { free(S); return OK; } /*输出串*/ void StrPrint(SString T) { for (int i = 1; i <= T[0]; i++) printf("%c",T[i]); printf("\n"); }
还有一个函数Index函数。做字符串匹配用,这里拿出来单独讨论
/*若主串S中存在和串T值相同的子串; /*则返回他在主串S中第pos个字符之后第一次出现的位置; /*否则函数值为0*/ int Index(SString S, SString T, int pos) { /*调用串基本操作的方法,也是Index函数的思想步骤*/ //if(pos > 0) //{ // int n = StrLength(S); //获取串的长度 // int m = StrLength(T); // int i = pos; // SString sub; // while(i <= n-m+1) //循环值从pos到串S的最后一个T长度位置 // { // SubString(sub,S,i,m); //获取子串,S的第i个位置开始长度m-1的子串 // if(StrCompare(sub,T) != 0) //判断获取的子串与串T是否不等;如果相等返回i的值也就是第一次出现的位置 // ++i; // else // return i; // } //} //return 0; //没有出现返回0 /*定位函数Index的模式匹配算法 /*算法的基本思想: /*从主串S的第pos个字符起和模式的第一个字符比较之, /*若相等,则继续逐个比较后续字符; /*否则从主串的下一个字符起再重新和模式的字符比较之。 /*以此类推,直至模式T中的每个字符依次和主串S中的一个连续的字符序列相等,则匹配成功; /*函数值为和模式T中的第一个字符相等的字符在主串S中的序号。 /*否则匹配不成功,函数值为零。*/ int i = pos,j = 1; while(i <= S[0] && j <= T[0]) { if(S[i] == T[j]) //继续比较后续字符 { i++; j++; } else //指针后退重新开始匹配 { i = i-j+2; j = 1; } } if(j > T[0]) return i - T[0]; //匹配成功 return 0; //匹配失败 }
其中这个函数内写了两种方法:第一种调用基本函数的方法,第二种模式匹配算法。
但模式匹配算法还有一个很是经典的算法模式匹配的改进算法-KMP算法;
其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右滑动尽可能远的一段距离后,继续进行比较。
KMP算法的基本思想就是这样,剩下的百度搜索全是我就不再细说,还是按我的习惯直接上代码。
void get_next(SString T, int *next) { int i=1; next[1]=0; int j=0; while (i<T[0]) { if(j==0 || T[i]== T[j]) { ++i; ++j; next[i] = j; } else j= next[j]; } } int Index_KMP(SString S, SString T, int pos) { // 利用模式串T的next函数求T在主串S中第pos个字符之后的位置的 // KMP算法。其中,T非空,1≤pos≤StrLength(S)。 int next[255]; int i = pos; int j = 1; get_next(T, next); while (i <= S[0] && j <= T[0]) { if (j == 0 || S[i] == T[j]) { // 继续比较后继字符 ++i; ++j; } else j = next[j]; // 模式串向右移动 } if (j > T[0]) return i-T[0]; // 匹配成功 else return 0; }
KMP算法时间复杂度O(m)。通常,模式串的长度m比主串的长度n要小的多,因此对整个匹配算法来说,所增加的这点时间是值得的。
但是,虽然模式匹配算法时间复杂度是O(n*m),但是一般情况下,其实际的实行时间近似于O(n+m),因此至今仍被采用。
KMP算法仅当模式与主串之间存在许多“部分匹配”的情况下才显得比模式匹配算法快得多。但是KMP算法的最大特点是指主串的指针不需回溯,整个匹配过程中,对主串仅需从头至尾扫描一遍,这对处理从外设输入的庞大文件很有效,可以边读入边匹配,而无需回溯重读。
但对于next函数前面所写的在某些情况下有一个缺陷,例如在模式aaaab,主串aaabaaaab匹配时,当i=4,j=4时ch[4]!=t.ch[4]。由next[j]的指示还需要进行i=4,j=3;i=4,j=2;i=4,j=1这三次比较。实际上,因为模式中第1,2,3个字符和第4个字符都相等,因此不必要在和主串中第4个字符相比较,而可以将模式一下子滑动4个位置。直接进行i=5,j=1时的字符比较。
j | 1 2 3 4 5 |
模式 | a a a a b |
next[j] | 0 1 2 3 4 |
nextval[j] | 0 0 0 0 4 |
void get_nextval(SString T, int *nextval) { // 求模式串T的next函数修正值并存入数组nextval。 int i = 1; int j = 0; nextval[1] = 0; while (i<T[0]) { if (j==0 || T[i]==T[j]) { ++i; ++j; if (T[i]!=T[j]) nextval[i] = j; else nextval[i] = nextval[j]; } else j = nextval[j]; } }
相关文章推荐
- 数据结构实验之栈四:括号匹配
- 01:数据结构和算法
- ES6入门——Set和Map数据结构
- 数据结构也不是那么没意思之后序二叉树+二叉树转伪双向循环链表
- 《数据结构》复习之二叉树
- 数据结构复习 - 图Map
- 数据结构之哈弗曼编码的(Huffman Coding)加密解密压缩
- 《Java数据结构与算法》笔记-CH5-链表-8实现双链表,头部和尾部插入和删除
- 数据结构 - 树Tree
- 数据结构——排序/搜索二叉树(递归)的基本操作实现
- 数据结构复习 - 队列Queue
- 数据结构复习 - 栈Stack
- 数据结构--AVL树
- Java千百问_06数据结构(025)_用二进制如何表示浮点型数值
- Java千百问_06数据结构(024)_用二进制如何表示整型数值
- 数据结构-线性表之单链表(Java实现)
- 数据结构复习——二叉树的几个基本操作
- Java千百问_06数据结构(023)_基本数据类型在内存中如何存放
- 数据结构和算法
- Java数据结构和算法——二叉查找树