【理解串】

一、串的基本概念

串:即字符串(string),是由零个或多个字符组成的有限序列,一般记为S = “a1a2a3a4...an”(n>=0)。其中,S是串名,双引号’'括起来的字符序列是串的值,a可以是字母、数字或其他字符,串中字符的个数n称为串的长度,当n=0时,称串为空串。
注意:单引号里只能放一个字符,双引号中可以放字符串,两个占用空间也有区别,示例:

#include<iostream>

using namespace std;

int main() {
    cout << "\'s\'的长度为:" << sizeof('s') << endl;
    cout << "\"s\"的长度为:" << sizeof("s") << endl;
    return 0;
}

运行结果如下:
在这里插入图片描述
因为"a"字符串结尾有一个’\0’字符,表示字符串结束,它也会占一个字节,而字符’a’只占一个字节。

子串:串中任意连续的字符组成的子序列;
主串:包含子串的串。

字符在主串中的位置:字符在串中的序号;
子串在主串中的位置:子串的首字符在主串中的位置。

串是一种特殊的线性表,数据元素之间呈线性关系,串的数据对象限定为字符集(中文字符、英文字符、数字字符、标点字符等)。

二、串的基本操作及实现

#include<iostream>
#define MaxSize 100

using namespace std;


//定长字符串
struct staiticString{
    char str[MaxSize + 1];//为什么要+1
    int length;//字符串长度
};

struct variableString{
    char* str;
    int length;
};

//字符串的基本操作
//串赋值
bool stringAssign(variableString& S, char* ch) {
    delete S.str;

    int length = 0;
    char* c = ch;

    while (*c != '\0')
    {
        length++;
        c++;
    }

    if (length == 0) {
        S.str = nullptr;
        S.length = 0;
        return true;
    }
    else {
        S.str = new char[length + 1];
        if (S.str == nullptr) {
            return false;
        }
        else {
            c = ch;
            for (int i = 0; i <= length; i++,++c) {
                S.str[i] = *c;
            }
            S.length = length;
            return true;
        }

    }
}

//获取字符串长度
int GetLength(variableString S) {
    return S.length;
}

//字符串比较
int stringCompare(variableString S1, variableString S2){
    for (int i = 0; i < S1.length && i < S2.length; ++i) {
        if (S1.str[i] != S2.str[i]) {
            return S1.str[i] - S2.str[i];
        }
    }
    //扫描过的所有字符都相同,则长度长的串更大
    return S1.length - S2.length;
}

//连接串,将串S1和串S2连接起来,并将连接结果返回到result
bool stringContact(variableString &result, variableString &S1, variableString S2) {
    delete result.str;
    result.str = nullptr;

    result.str = new char[S1.length + S2.length + 1];
    if (result.str == nullptr) {
        cerr << "Memory is not enough!" << endl;
        return false;
    }
    //将S1的内容先放到result里
    int i = 0;
    while (i<S1.length) {
        result.str[i] = S1.str[i];
        i++;
    }
    //再把S2的内容放到紧接着S1内容的后面
    int j = 0;
    while (j<S2.length) {
        result.str[i + j] = S2.str[j];
        j++;
    }
    result.length = S1.length + S2.length;

    return true;
}

//求子串,其中from为子串的起始位置,length为子串的长度
bool subString(variableString &reslut, variableString S1,int from, int length) {
    //判断给定参数的合法性
    if (from<0||from>S1.length||length<0||length>S1.length-from) {
        cerr << "Parameters are wrong" << endl;
        return false;
    }

    delete reslut.str;
    reslut.str = nullptr;

    if (length ==0) {
        reslut.str = nullptr;
        reslut.length = 0;
        return true;
    }
    else {
        reslut.str = new char[length + 1];
        int i = from;
        int j = 0;
        while (i<from+length)
        {
            reslut.str[j++] = S1.str[i++];
        }

        reslut.str[j] = '\0';
        reslut.length = length;
        return true;
    }

}

//清空串
bool clearString(variableString &S) {
    delete S.str;
    S.str = nullptr;
    S.length = 0;
    return true;
}

int main() {
    variableString S1{};
    char ch[MaxSize] = {'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', '!', '\0'};
    //调用串赋值
    stringAssign(S1,ch);
    for (int i = 0; i < S1.length + 1; ++i) {
        cout << S1.str[i];
    }
    cout << endl;

    variableString S2{};
    //S2.str = ch;
    //S2.length = S1.length;
    char ch1[MaxSize] = { 'h','e','l','l','o','\0'};
    stringAssign(S2,ch);

    //调用串比较
    if (stringCompare(S1, S2) == 0) {
        cout << "S1 equals S2" << endl;
    }else if(stringCompare(S1,S2)>0){
        cout << "S1 is bigger than S2" << endl;
    }
    else {
        cout << "S1 is smaller than S2" << endl;
    }

    //串连接
    variableString result1{};
    stringContact(result1,S1,S2);
    while (*result1.str != '\0')
    {
        cout << *result1.str++;
    }
    cout << endl;

    //求子串
    variableString result2{};
    subString(result2,S1,1,8);
    while (*result2.str != 0)
    {
        cout << *result2.str++;
    }
    cout << endl;
    if (clearString(S1)) {
        cout << "S1 has been cleared!" << endl;
    }

    return 0;
}

三、串的存储实现

3.1、静态数组实现

#define MaxSize 100

typedef struct staticString{
     char ch[MaxSize];
     int length;
};

3.2、动态数组实现

typedef struct variableString{
    char *ch;
    int length;
};

四、串的朴素模式匹配

串的模式匹配:
在主串中找到与模式串相同的子串,并返回其所在主串中的位置。

4.1、算法思想

  1. 先将主串中与模式串长度相同的子串找出来,挨个与模式串对比,当所比子串与模式串某个对应字符不匹配时,就立即放弃当前子串,转而检索下一个子串;
  2. 若模式串长度为m,主串长度为n,则直到匹配成功/匹配失败最多需要进行(n-m+1)*m次,最坏时间复杂度为:O(mn);
  3. 最坏情况:每个子串的前m-1个字符都与模式串匹配,只有第m个字符不匹配;
  4. 比较好的情况:每个子串的第1个字符就与模式串匹配。

4.2、代码实现

//S为主串,cs为模式串(子串)
int Index(string S, string cs){
    int k = 1;
    int i = k, j = 1;
    while(i<=S.length && j<= cs.length){
        if(S.str[i] == cs.str[j]){
            ++i, ++j;
        }else{
            k++,i=k,j+1;
        }
    }
    if(j>cs.length){
        return k;
    else
        return 0;
}

或者不用k的方法:

int Index(string S, string cs){
    int i=0;//扫描主串
    int j=0;//扫描模式串
    while(i<S.length && j<cs.length){
        if(S.str[i] == cs.str[i]){
            ++i;
            ++j;//继续比较后续字符
        }else{
        i = i-j + 1;//指针后退,重新开始匹配
        j = 1;
        }
    }
    if(j>cs.length)
    return i-cs.length;
    else return -1;
}

匹配成功的最好时间复杂度为:O(m);
匹配失败的最好时间复杂度为:O(n);
最坏时间复杂度为:O(mn);

五、KMP算法

5.1、算法思想

朴素模式串匹配算法的缺点:当某些子串与模式串部分匹配时,主串的扫描指针i经常回溯,导致时间开销增加;
KMP算法:当子串和模式不匹配时,主串指针不回溯,模式串指针j=next[j],算法平均时间复杂度:O(m+n)。

5.2、求模式串的next数组

  1. 串的前缀:包含第一个字符,且不包含最后一个字符的子串;
  2. 串的后缀:包含最后一个字符,且不包含第一个字符的子串;
  3. 当第i个字符匹配失败时,由前1~j-i个字符组成的串记为s,next[i]=s的最长前后缀长度+1,特别地:规定next[1]=0;

5.2、代码实现

//获取next数组
void getNext(SString SS, int next[]){
    int i=1, j=0;
    next[1]=0;
    while(i<SS.length){
        if(j==0||SS.str[1]==SS.str[j]{
            ++i,++j;
            next[i]=j;
        }else{
            j=next[j];
        }
    }
}

//KMP算法,求主串中模式串的位序,没有则返回0
int Index_KMP(string S, string cs){
    int i=1,j=1;
    int next[cs.length+1];
    getNext(cs,next);
    while(i<=S.length || j<=cs.length){
        if(j==0 || S.str[i] == cs.str[j]){
            ++i,++j;
        }else{
            j=next[j];
        }
    }
    if(j>cs.length)
        return i-cs.length;
     else return 0;
}

int main(){
    SString S={"ababcabcd", 9};
	SString T={"bcd", 3};
	printf("%d ", Index_KPM(S, T));	//输出9
}

KMP算法的进一步优化,改进next数组:

void getNextval(SString T, int nextval[]){
    int i=1,j=0;
    nextval[1]=0;
    while(i<T.length){
        if(j==0 || T.ch[i]==T.ch[j]){
            ++i; ++j;
            if(T.ch[i]!=T.ch[j])
                nextval[i]=j;
            else
                nextval[i]=nextval[j];
        }else
            j=nextval[j];
    }
}
  • 29
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值