数据结构与算法(C语言)代码实现-串的相关操作代码实现(顺序串)

顺序串

    • 串的概念
    • 顺序串的图示
    • KMP算法
    • 实现的操作概览
    • C代码实现

串的概念

串是由0个或多个字符组成的有限序列,例如:

s="abcdef"

s是串名,双引号里面的是串值。
串的长度:双引号里面字符的个数(空格也算一个字符)
空串:字符个数为0,例如

s=""

空格串:双引号里面的字符只有空格(一个或多个),不要与空串混淆,例如

s="                            "

子串:串中任意连续的字符组成的子序列称为该串的子串
主串:相对于该子串来说,包含这个子串的另一个串称为主串
有三个字符串s1、s2和s3,s2是s1的子串,s1被称为s2的主串。s3不是s1的子串

s1="qwerty"
s2="wer"
s3="qer"

顺序串的图示

数据结构与算法(C语言)代码实现-串的相关操作代码实现(顺序串)_第1张图片

KMP算法

数据结构与算法(C语言)代码实现-串的相关操作代码实现(顺序串)_第2张图片

实现的操作概览

  • 串赋值,将字符数组的元素赋值给串
    StrAssign(String &str, char chars[]);

  • 串比较,str1的ascii码比str2的大就返回1,相等返回0,小于返回-1
    StrCompare(String str1, String str2);

  • 求串长
    StrLength(String str);

  • 串连结,将str1和str2连接,返回给str
    Concat(String &str, String str1, String str2);

  • 求子串,在str中截取从指定位置开始,指定长度的子串,并返回给subStr
    SubString(String &subStr, String str, int startPoint, int len);

  • 串拷贝
    StrCopy(String &str1, String str2);

  • 串判空
    StrEmpty(String str);

  • 清空串
    ClearString(String &str);

  • 获取子串的位置,使用DF算法,既暴力穷举法,是子串返回第一个字符出现的位置,找不到返回0
    index_DF(String str, String T, int point);

  • 获取子串的位置,kmp算法查找,找到返回模式串第一个字符出现在主串中的位置,找不到返回0
    index_KMP(String str, String T, int point);

  • 子串的插入,将子串subStr插入到str指定位置中
    StrInsert(String &str, int insertPoint, String subStr);

  • 子串的删除
    StrDelete(String &str, int startPoint, int len);

  • 串销毁
    DestroyString(String &str);

  • 打印串
    printStr(String str);

  • 计算出字符串的公共前缀表
    prefix_table(String str, int prefix[]);

C代码实现

//###一些常量
//函数结果状态码
#include 

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//串的最大容量
#define MAXSIZE 100
//函数类型
typedef int Status;

typedef struct {
    //存储串的最大度,第ch[0]位置不存储元素
    char ch[MAXSIZE + 1];
    //记录串的长度
    int length;
} String;

//------------------------------------
//串赋值,将字符数组的元素赋值给串
void StrAssign(String &str, char chars[]);

//串比较,str1的ascii码比str2的大就返回1,相等返回0,小于返回-1
int StrCompare(String str1, String str2);

//求串长
int StrLength(String str);

//串连结,将str1和str2连接,返回给str
void Concat(String &str, String str1, String str2);

//求子串,在str中截取从指定位置开始,指定长度的子串,并返回给subStr
Status SubString(String &subStr, String str, int startPoint, int len);

//串拷贝
void StrCopy(String &str1, String str2);

//串判空
Status StrEmpty(String str);

//清空串
void ClearString(String &str);

//获取子串的位置,使用DF算法,既暴力穷举法,是子串返回第一个字符出现的位置,找不到返回0
int index_DF(String str, String T, int point);

//获取子串的位置,kmp算法查找,找到返回模式串第一个字符出现在主串中的位置,找不到返回0
int index_KMP(String str, String T, int point);

//子串的插入,将子串subStr插入到str指定位置中
Status StrInsert(String &str, int insertPoint, String subStr);

//子串的删除
Status StrDelete(String &str, int startPoint, int len);

//串销毁
void DestroyString(String &str);

//打印串
void printStr(String str);

//计算出模式串的公共前缀表
void prefix_table(String str, int prefix[]);

//------------TEST-------------
int main() {
//    创建一个串
    String string;
//    声明一个内容为HelloWorld的字符数组
    char *data = new char[10]{'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'};
//    赋值
    StrAssign(string, data);
    printf("string串的长度:%d\n", StrLength(string));//9
    // 判断串是否为空
    printf("string串是否为空:%d\n", StrEmpty(string));//0
    printf("string串:");
    printStr(string);
//    创建另外一个串
    String string2;
//    声明一个内容为HelloWorld的字符数组
    char *data2 = new char[10]{'W', 'o', 'r', 'l', 'd'};
//    赋值
    StrAssign(string2, data2);
    printf("string2串:");
    printStr(string2);
    //比较string和string2两个串的ASCII码值大小
    printf("string串和string2串的大小(1为string大,0为相等,-1为string小)"
           ":%d\n", StrCompare(string, string2));//1
    //获取string2在string出现的位置
    printf("string2在string出现的位置(1):%d\n", index_DF(string, string2, 1));//6
    //子串删除
    StrDelete(string, index_DF(string, string2, 1), StrLength(string2));
    //获取string2在string出现的位置
    printf("string2在string出现的位置(2):%d\n", index_DF(string, string2, 1));//0
    //拷贝
    String string3;
    StrCopy(string3, string);
    printf("string3的长度:%d\n", StrLength(string3));//5
    printf("string3串:");
    printStr(string3);
    // 串连结
    String string4;
    Concat(string4, string3, string3);
    printf("string4的长度:%d\n", StrLength(string4));//10
    printf("string4串:");
    printStr(string4);
    //将string2插入到string4中
    StrInsert(string4, 4, string2);
    printf("string4串:");
    printStr(string4);
    //从string4中截取出子串
    String string5;
    SubString(string5, string4, 2, 8);
    //串销毁
    DestroyString(string);
    printStr(string);
    //
    StrAssign(string, new char[12]{'C', 'A', 'B', 'A', 'A', 'B', 'A', 'B', 'A', 'C', 'A'});
    String str;
    StrAssign(str, new char[10]{'A', 'C', 'A'});
    int point = index_KMP(string, str, 1);
    printf("出现的位置:%d", point);//9
}
//-----------------------------

/**
 * 打印串
 * @param str
 */
void printStr(String str) {
    if (StrEmpty(str)) {
        printf("空串\n");
        return;
    }
    for (int i = 1; i <= str.length; i++) {
        printf("%c", str.ch[i]);
    }
    printf("\n");
}

/**
 * 获取子串的位置,使用DF算法,既暴力穷举法,是子串返回第一个字符出现的位置,不是返回0
 * @param str 主串
 * @param temStr 用来对比的模板串
 * @param startPoint 从主串中开始匹配的位置
 * @return temStr是子串:返回temStr的第一个字符在str中出现的位置
 */
int index_DF(String str, String temStr, int startPoint) {
    //开始查找位置校验
    if (startPoint < 1 || startPoint > str.length) {
        printf("开始查找的位置有误");
        return 0;
    }
    for (int i = startPoint, j = 1; i <= str.length && j <= temStr.length;) {
        if (str.ch[i] != temStr.ch[j]) {
            //主串上的字符和模板串上的字符不相等时
            //主串位置回溯
            i = i - j + 2;
            //子串回到第一个
            j = 1;
            //结束本次循环,从模板串第一个字符开始比较
            continue;
        }
        //两个位置的字符相等,下一次对比的位置都后移
        i++, j++;
        //子串已经完了,且前面每一个位置都相等,返回模板串第一个字符在主串中出现的位置
        if (j > temStr.length) {
            return i - temStr.length;
        }
    }
    //不是子串,返回0
    return 0;
}

/**
 * kmp算法查找,找到返回模式串第一个字符出现在主串中的位置,找不到返回-1
 * @param str 主串
 * @param temStr 模式串
 * @param point 在主串中开始查找的位置
 * @return
 */
int index_KMP(String str, String temStr, int point) {
    //校验开始查找的位置是否正确
    if (point < 1 || point > str.length) {
        printf("point值的范围不正确");
        return 0;
    }
    //    printStr(temStr);
    //定义出公共前缀表
    int prefix[temStr.length];
    //根据串求出这个串的公共前缀表
    prefix_table(temStr, prefix);
    //打印公共前缀表
//    for(int i = 0; i
//        printf("%d\t", prefix[i]);
//    }
    //使用kmp算法进行查找
    //i指向主串,j指向模式串
    int i = point, j = 1;
    while (i <= str.length && j <= temStr.length) {
        if (str.ch[i] == temStr.ch[j]) {
            //相同的话,两个位置都后移
            i++;
            j++;
        } else {
            //两个位置出现不相等
            //模式串根据公共前缀表回溯j指向的位置
            if (prefix[j - 1] != -1) {
                //
                j = prefix[j - 1] + 1;
            } else {
                j = 1;
                i++;
            }
        }
        //根据j的位置进行判断是否找到
        if (j > temStr.length) {
            return i - temStr.length;
        }
    }

    return 0;
}

/**
 * 计算出模式串的公共前缀表
 * @param str 模式串
 * @param prefix 接收公共前缀表
 */
void prefix_table(String str, int prefix[]) {
    //第一个字符,公共前缀为0
    prefix[0] = 0;
    //公共前缀的长度
    int len = 0;
    //从串的第二个字符开始,第一个字符公共前缀是0
    int i = 2;
    //计算出公共前缀表
    while (i <= str.length) {
        if (str.ch[i] == str.ch[prefix[i - 2] + 1]) {
            prefix[i - 1] = ++len;
            i++;
        } else {
            if (len > 0) {
                if (str.ch[i] == str.ch[prefix[len - 1] + 1]) {
                    len = prefix[len - 1] + 1;
                    prefix[i - 1] = len;
                    i++;
                } else {
                    len--;
                }
            } else {
                prefix[i - 1] = 0;
                i++;
            }
        }
    }
    //去掉公共前缀表的最后一个,并将剩余元素依次往后移一个位置,第一个位置的公共前缀的长度设为-1
    for (int i = str.length - 1; i >= 0; i--) {
        if (i == 0) {
            prefix[i] = -1;
        } else {
            prefix[i] = prefix[i - 1];
        }

    }
}


/**
 * 求串长
 * @param str
 * @return 串的长度
 */
int StrLength(String str) {
    return str.length;
}


/**
 * 串赋值,将字符数组的元素赋值给串
 * @param str
 * @param chars
 */
void StrAssign(String &str, char chars[]) {
    //清空
    ClearString(str);
    //将字符数组的每个元素放到串的数组中
    for (int i = 1; chars[i - 1] != '\0' && i < MAXSIZE; i++) {
        //放入数组中
        str.ch[i] = chars[i - 1];
        //长度加1
        str.length++;
    }
}


/**
 * 清空串,将串的每个元素置为空
 * @param str 要清空的串
 */
void ClearString(String &str) {
//    if(str.length){
//        //存储串的数组的第一个元素不用来存储
//        for(int i = 1; i <= str.length || str.ch[i] != '\0'; i++){
//            //将每个元素都设置为空
//            str.ch[i] = '\0';
//        }
//        //将数组的长度置为0
//        str.length = 0;
//    }
    str.length = 0;

}

/**
 * 判断串是否为空
 * @param str 串
 * @return
 */
Status StrEmpty(String str) {
    return str.length ? FALSE : TRUE;
}

/**
 * 串销毁
 * @param str 串
 */
void DestroyString(String &str) {
    ClearString(str);
}

/**
 * 子串的插入,将子串subStr插入到str指定位置中
 * @param str 被插入的串
 * @param insertPoint 插入的位置
 * @param subStr 要插入的子串
 * @return
 */
Status StrInsert(String &str, int insertPoint, String subStr) {
    //判断是否有插入的空间
    if (str.length + subStr.length > MAXSIZE) {
        printf("插入失败,串现有的剩余空间不足以存放子串!");
        return ERROR;
    }
    //如果插入的位置在串的最后一个元素的后面,将插入的位置都调到串的最后一个字符之后
    if (insertPoint > str.length) {
        insertPoint = str.length + 1;
    }
    //判断插入位置是否正确
    if (insertPoint < 1 || subStr.length + insertPoint > MAXSIZE) {
        printf("插入的位置不正确");
        return ERROR;
    }
    //计算出最后插入的位置
    int endPoint = insertPoint + subStr.length - 1;
    //将要插入位置的元素后移,到指定位置就插入
    for (int i = str.length; i >= insertPoint; i--) {
        str.ch[i + subStr.length] = str.ch[i];
        //到插入的位置了,将元素插入
        if (i == endPoint) {
            str.ch[i] = subStr.ch[(endPoint--) - insertPoint + 1];
        }
    }
    str.length += subStr.length;
    return TRUE;
}

/**
 * 子串的删除
 * @param str 串
 * @param startPoint 要开始删除的位置
 * @param len 删除的长度
 */
Status StrDelete(String &str, int startPoint, int len) {
    //计算出要删除子串的最后一个位置
    int endPoint = startPoint + len - 1;
    //判断要删除的位置是否正确
    if (startPoint < 1 || endPoint > str.length) {
        printf("要删除的位置有误\n");
        return ERROR;
    }
    //
    //将要删除的位置的后面的字符覆盖删除的位置的字符
    for (int i = endPoint + 1; i <= str.length; i++) {
        str.ch[startPoint++] = str.ch[i];
    }
    str.length -= len;
    return OK;
}

/**
 * 串比较,str1的ascii码比str2的大就返回1,相等返回0,小于返回-1
 * @param str1 串
 * @param str2 串
 * @return
 */
int StrCompare(String str1, String str2) {
    //计算出每个串的ASCII码总值
    int str1Ascii = 0;
    int str2Ascii = 0;
    //最大循环次数,取决于最长的串
    int max = str1.length > str2.length ? str1.length : str2.length;
    for (int i = 1; i <= max; i++) {
        //计算str1的ASCII码
        if (i <= str1.length) {
            str1Ascii += str1.ch[i];
        } else {
            //str1算完之后,要是小于str2的,直接结束循环
            if (str1Ascii < str2Ascii) break;
        }
        //计算str2的ASCII码
        if (i <= str2.length) {
            str1Ascii += str2.ch[i];
        } else {
            //str2算完之后,要是小于str1的,直接结束循环
            if (str1Ascii > str2Ascii) break;
        }
    }
    if (str1Ascii > str2Ascii) {
        return 1;
    } else if (str1Ascii < str2Ascii) {
        return -1;
    } else {
        //相等情况
        return 0;
    }
}

/**
 * 串连结,将str1和str2连接,返回给str
 * @param str 接收结果的串
 * @param str1 用来拼接的串1
 * @param str2 用来拼接的串2
 */
void Concat(String &str, String str1, String str2) {
    //先将str串清空
    ClearString(str);
    //读取str1串到str
    for (int i = 1; i <= str1.length; i++) {
        str.ch[i] = str1.ch[i];
        str.length++;
    }
    //读取str2串到str
    for (int i = (str1.length + 1); i <= (str1.length + str2.length); i++) {
        str.ch[i] = str2.ch[i - str1.length];
        str.length++;
    }
}

/**
 * 求子串,在str中截取从指定位置开始,指定长度的子串,并返回给subStr
 * @param subStr
 * @param str
 * @param startPoint
 * @param len
 */
Status SubString(String &subStr, String str, int startPoint, int len) {
    //截取位置校验
    if (startPoint + len > str.length || startPoint < 1) {
        printf("位置出错了");
        return ERROR;
    }
    //将子串清空
    ClearString(subStr);
    //读取str指定位置的字符到subStr中
    for (int i = startPoint; i <= startPoint + len; i++) {
        subStr.ch[i - startPoint] = str.ch[i];
        subStr.length++;
    }
    return OK;
}

/**
 * 串拷贝,将str2拷贝到str1
 * @param str1
 * @param str2
 */
void StrCopy(String &str1, String str2) {
    //清空str1
    ClearString(str1);
    //拷贝
    for (int i = 1; i <= str2.length; i++) {
        str1.ch[i] = str2.ch[i];
        str1.length++;
    }
}


你可能感兴趣的:(c语言,算法,c++,链表,数据结构)