简书内代码已上传GitHub:点击我 去GitHub查看代码
3 / 29 更新 基本操作写了些注意的点,把一些原本模仿书上写的代码进行了修改...书太坑了(国内教材)...
一串葡萄
一.顺序串及其结构定义
顺序串类似于线性表的顺序存储结构,都是用一组地址连续的存储单元存储串值。
因为是定长的顺序串,所以按照我们按照预定义的大小,为每个串变量分配固定长度的存储区,结构如下:
// 用户可在255以内定义最大串长
#define MAXSTRLEN 255
// 0号单元存放串的长度, 故长度为MAXSTRLEN + 1
typedef unsigned char SString[MAXSTRLEN + 1];
PS:这里一定要清楚typedef的用法,这样定义后,举个例子:
SString a 就等价于 char a[MAXSTRLEN + 1]
二. 顺序串的基本操作(13种)
生成串:
因为是定长串,所以需要考虑长度超限的情况
T[0]存储串长
// 生成一个值等于chars的串
Status StrAssign(SString T, char chars[]){
// 长度超限
if(strlen(chars) > MAXSTRLEN){
//printf("error: too long to assign\n");
return ERROR;
}else{
// 存放长度
T[0] = strlen(chars);
//复制
for(int i = 1 ; i <= T[0] ; ++i)
T[i] = *(chars + i - 1);
return OK;
}
}
复制串
按元素依次赋值即可
//由串S复制得到串T
Status StrCopy(SString &T, SString S){
for(int i = 0 ; i <= S[0] ; ++i){
T[i] = S[i];
}
return OK;
}
3.判断串是否为空
串的长度为0, 返回True, 长度不为0,返回False
// 判断S是否为空
Status StrEmpty(SString S){
return S[0] == 0 ? True : False;
}
比较串的大小
比较串的大小其实就是比较ASCII的大小
从前到后按元素依次比较,比如abcdefg < abddefg
S > T, 返回值 > 0, S == T , 返回值= 0, S < T, 返回值 < 0
// 比较串的大小, 如果S > T , 返回值 > 0; 如果S == T, 返回 0;如果S < T ,返回值 < 0
Status StrCompare(SString S, SString T){
int i;
for(i = 1 ; i <= S[0] && i <= T[0] ; ++i){
if(S[i] != T[i]) return S[i] - T[i];
}
return S[0] - T[0];
}
返回串的长度
返回S[0]的值,对数据进行封装
// 返回串的长度
int StrLength(SString S){
return S[0];
}
清空串
将S[0]置空,相当于对已有数据做上标记 - 空
DestroyString等同于ClearString
// 清空串
Status ClearString(SString &S){
S[0] = 0;
return OK;
}
连接两串,生成新串
分为截断和未截断两种可能性
S1 + S2 < T 则不发生截断, S1 + S2 > T 发生截断
截断还需要考虑是只有S2截断还是S1截断....
其实感觉合并成一种情况就行了,死板的学着教材还是...不大好
// 连接两串返回新串,未截断返回True,截断返回False
Status Concat(SString &T, SString S1, SString S2){
// 未截断
if(S1[0] + S2[0] <= MAXSTRLEN){
T[0] = S1[0] + S2[0];
// S1
for(int i = 1 ; i <= S1[0] ; ++i){
T[i] = S1[i];
}
// S2
for(int j = S1[0] + 1 ; j <= T[0] ; ++j){
T[j] = S2[j - S1[0]];
}
return True;
// S2截断 / 完全截断
}else if(S1[0] <= MAXSTRLEN){
T[0] = MAXSTRLEN;
// S1先赋给T
for(int i = 1 ; i <= S1[0] ; ++i){
T[i] = S1[i];
}
// S2剩余部分
for(int j = S1[0] + 1 ; j <= MAXSTRLEN ; ++j){
T[j] = S2[j - S1[0]];
}
return False;
}
/*
// S1、S2截断或者S2完全截断
}else{
T[0] = MAXSTRLEN;
// S1
for(int i = 1 ; i <= T[0] ; ++i){
T[i] = S1[i];
}
return False;
}
*/
}
用Sub返回特定子串
注意pos的越界判定
// 用Sub返回特定子串(pos开始,长度为len)
int SubString(SString &Sub, SString S, int pos, int len){
if((pos < 1) || (pos > S[0]) || (len < 0) || (len > S[0] - pos + 1))
return ERROR;
Sub[0] = len;
for(int i = pos ; i < pos + len ; ++i){
Sub[i] = S[i];
}
return OK;
}
模式匹配
还是用的Sunday算法,简单好懂,需要的移步 Sunday算法
// 返回子串在主串pos后第一次出现的位置(Sunday)
Status Index(SString S, SString T, int pos){
int len1 = S[0], len2 = T[0];
// 获得偏移量数组
int * next = (int*)malloc(sizeof(int) * len2);
for(int k = 0 ; k < len2 ; ++k){
next[k] = len2 - k;
}
int i = pos, j = 0;
while(i < len1 && j < len2){
// 依次判断,相等 i 、 j自增
if(S[i + 1] == T[j + 1]){
++i;
++j;
}else{
// 如果索引下标超出范围,结束搜索
if(i + len2 - j > len1)
break;
// 获得元素在T串中的索引(逆序第一个)
int index = -1;
for(int l = len2 - 1 ; l >= 0 ; --l){
if(S[i + len2 - j] == T[l]){
index = l;
break;
}
}
// 元素在串内,按偏移量数组进行偏移
if(index != -1){
i += (next[index] - j);
j = 0;
}else
// 不在串内,直接整体移到该元素后
i += len2 + 1 - j;
}
}
// 成功匹配
if(j == len2)
return i - len2;
return 0;
}
插入
和concat一样分为完全插入和部分插入
同样注意边界问题
// 插入
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] += T[0];
return True;
}else{
// 部分插入
// 若T完整插入,在T后插入剩余S
for(int i = MAXSTRLEN ; i >= pos + T[0] ; --i){
S[i] = S[i - T[0]];
}
// pos处插入T
for(int i = pos ; i < pos + T[0] && i <= MAXSTRLEN ; ++i){
S[i] = T[i - pos + 1];
}
S[0] = MAXSTRLEN;
return False;
}
}
删除
边界问题
删除后连接分离的子串
// 删除指定位置指定长度子串
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;
}
串的替换
简单的来说就是 定位子串,删除子串,插入子串,如此循环,直到没有能匹配的子串为止
把Replace放在插入和删除后面是由原因的,这样可以简单的利用插入和删除操作来实现replace
// 用V替换S中所有出现的T
Status Replace(SString &S, SString T, SString V) {
// 从串S的第一个字符起查找串T
int i = 1;
Status k;
// 如果T是空串 ,返回错误
if(StrEmpty(T)) return ERROR;
do{
// 保存i后第一个子串的位置
i = Index(S, T, i);
// 若找到子串
if(i) {
// 删除T
StrDelete(S, i, StrLength(T));
// 插入V
k = StrInsert(S, i, V);
// 不能完全插入
if(!k)
return ERROR;
// 查找下一个T
i += StrLength(V);
}
}while(i);
return OK;
}
13.输出
遍历,打印
// 输出字符串T。
void StrPrint(SString T) {
for( int i = 1 ; i <= T[0] ; ++i )
printf("%c ", T[i]);
printf("\n");
}
三.串的应用
文本编辑
建立词索引表
PS:未完待续