數據結構筆記四:串


串的定義

,即字符串(String)是由兩個或多個字符組成的有序序列。一般記為 \(S='a_1a_2\cdot \cdot \cdot a_n'\)

其中,S是串名,單引號括起來的字符序列是串的值;\(a_i\)可以是字符、數字或其他字符;

串中字符的個數n稱為串的長度。\(n=0\)時的串稱為空串(用\(\varnothing\)表示)

子串:串中任意個連續的字符組成的子序列

主串:包含子串的串

字符在主串的位置:字符在串中的序號

子串在主串的位置:子串的第一個字符在主串的位置

串是一種特殊的線性表,數據對象限定為字符集。基本操作以子串為操作對象

串的存儲結構

定長順序存儲表示

//定長數組
#define MAXLEN 255		//預定義最大串長為255
typedef struct{
    char ch[MAXLEN];	//每個分量存儲一個字符
    int length;		    //串的實際長度
}SString;

image-20210816214335451

堆分配存儲表示

//動態分配
typedef struct{
    char ch*;		    //按串長分配存儲區,ch指向串的基地址
    int length;		    //串的長度
}HString;

塊鏈存儲表示

typedef struct StringNode{
    char ch;		    //每個結點存一個字符
    struct StringNode* next;
}StringNode,*String;
//比較推薦
typedef struct StringNode{
    char ch[4];		    //每個結點存多個字符
    struct StringNode* next;
}String;

串的基本操作

StrAssign(&T,chars);		//賦值操作,把串T賦值為chars
StrCopy(&T,S);			    //復制操作,由串S復制得到串T
StrEmpty(S);			    //判空操作,若S為空串,則返回TRUE,否則返回FALSE
StrLength(S);				//求串長,返回串S的元素個數
ClearString(&S);			//清空操作,將S清為空串
DestoryString(&S);			//銷毀串。將串S銷毀(回收存儲空間)
Concat(&T,S1,S2);			//串聯接。用T返回由S1和S2聯接而成的新串
SubString(&Sub,S,pos,len);	 //求子串。用Sub返回串S的第pos個字符起長度為len的子串
Index(S,T);				    //定位操作。若主串S存在與串T值相同的子串,則返回他在主串S第						   //一次出現的位置
StrCompare(S,T);			//比較操作。若S>T,則返回值>0;若S=T,則返回值=0;
    					    //若S<T,則返回值<0
    					    //從第一個字符開始往后依次對比,先出現更大字符的串就更大
    					    //長串的前綴與短串相同時,長串更大

求子串

bool SubString(SSting &Sub,SSting S,int pos,int len)
{
    //子串范圍越界
    if(pos+len-1>S.length)
        return false;
    for(int i=pos;i<pos+len;i++)
        Sub.ch[i-pos+1]=S.ch[i];
    Sub.length=len;
    return false;
}

比較操作

int StrCompare(SSting S,SSting T)
{
    for(int i=1;i<=S.length&&i<=T.length;i++)
    {
        if(S.ch[i]!=T.ch[i])
            return S.ch[i]-T.ch[i];
    }
    //掃描過的所有字符都相同,則長度長的串更大
    return S.length-T.length;
}

定位操作

int Index(SString S,SSting T)
{
    int i=1,n=StrLength(S),m=StrLength(T);
    SString sub;
    while(i<=n-m+1)
    {
        SubString(sub,s,i,m);
        if(StrCompare(sub,T)!=0)
            ++i;
        else
            return i;				//返回子串在主串中的位置
    }
    return 0;					    //S中不存在與T相等的子串
}

模式匹配算法

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

朴素模式匹配算法

image-20210816221436464

int Index(SStromh S,SString T)
{
    int i=1,j=1;
    while(i<=S.length&&j<=T.length)
    {
        if(S.ch[i]==T.ch[i])
        {
            ++i;++j;
        }
        else
        {
            i=i-j+2;
            j=1;					//指針后退重新開始匹配
        }
    }
    if(j>T.length)
        return i-T.length;
    else
        return 0;
}

性能分析

較好情況:每個子串第一個字符就與模式串不匹配

image-20210816222404187

若模式串長度為m,主串長度為n,則

匹配成功的最好時間復雜度:\(O(m)\)

匹配失敗的最好時間復雜度:\(O(n-m+1)=O(n-m)=O(n)\)

較壞情況:

image-20210816222249165

若模式串長度為m,主串長度為n,則直到匹配成功/匹配失敗最多需要\((n-m+1)*m\)次比較

最壞時間復雜度:\(O(nm)\)

KMP算法

朴素模式匹配算法的缺點:當某些子串與模式串部分匹配時,主串的掃描指針\(i\)​經常回溯,導致時間開銷增加。

改進思路:主串指針不回溯,只有模式串指針回溯。

如果\(j=k\)時才發現匹配識別,說明\(1到k-1\)都匹配成功

image-20210816224520637

int Index_KMP(SString s,SString T,int next[])
{
    int i=1;j=1;
    while(i<=S.length&&j<=T.length)
    {
        if(j==0||S.ch[i]==T.ch[j])
        {
            ++i;
            ++j;					//繼續比較后繼字符
        }
        else
        {
            j=next[j];
        }
    }
    if(j>T.length)
        return i-T.length;
    else 
        return 0;
}

next數組:當模式串的第\(j\)個字符匹配失敗時,令模式串跳到\(next[j]\)再繼續匹配

串的前綴:包含第一個字符,且不包含最后一個字符的子串

串的后綴:包含最后一個字符,且不包含第一個字符的子串

當第\(j\)個字符匹配失敗,由前\(1到j-1\)個字符組成的串記為\(S\),則:\(next[j]=s\)的最長相等前后綴長度+1

特別的,\(next[1]=0\)

image-20210817214246158

//求next數組
void get_next(SString T,int next[])
{
    int i=1,j=0;
    next[1]=0;
    while(i<T.length)
    {
        if(j==0||T.ch[i]==T.ch[j])
        {
            ++i;++j;
            //若pi=pj,則next[j+1]=next[j]+1;
            next[i]=j;
        }
        else
        {
            //否則令j=next[j],循環繼續
            j=next[j];
        }
    }
}

KMP算法性能分析:當子串和模式串不匹配時,主串指針i不回溯,模式串指針\(j=next[j]\)算法平均時間復雜度:\(O(m+n)\)

KMP算法優化

image-20210903211538528

當子串和模式串不匹配時\(j=nextval[j]\)(減少對比)

//計算next數組修正值
void get_nextval(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];
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM