数据结构 (六) 串的应用

数据结构 (六) 串的应用

串的基本操作

串的最小操作子集
  1. 串赋值StrAssign

    String StrAssign(String str,char s[]) 
    {
        int i=0;
        while(s[i])
        {
            str->ch[++str->length]=s[i];
            i++;
        }
        return str;
    }
    
  2. 串比较StrCompare

    void StrCompare(String *s1,String *s2)
    {
        int i;
    	if (s1->length!=s2->length)
    	{
            printf("两个串不相同\n");
    	}
        else
    	{
            for(i=0;i<s1->length;i++)
            {
                if (s1->data[i]!=s2->data[i])
                {
                    printf("两个串不相同\n");
                    return;
                }
            }
            printf("两个串相同\n");
        }
    	return;
    } 
    
  3. 求串长StrLength

    int getStrLength(String str)	
    {
        return str->length;
    }
    
  4. 串联接Concat

    //C++  使用 + 运算符
    string res = str1 + str2;
    //C++  使用 strcat()函数
    strcat(str1, str2);
    //C ++中用于字符串连接的append()
    string1.append(string2);
    //使用循环逐个字符添加
    #include<iostream>
    #include<string.h>
    using namespace std;
    
    int main()
    {
     
    	char x[100]="Hello", y[100]="World";
    	int i;
    	for(i=0; x[i] != '\0'; i++);//pointing to the index of the last character of x
    	
    	for(int j=0; y[j] != '\0'; j++,i++)
    	{
    		x[i]=y[j];
    	}
    	
    	x[i]='\0';
        cout<<"Concatenated String:\n";
    	cout<<x<<endl;
    	
    	return 0;
    }
    
    
  5. 求子串SubString

    String SubString(String *s,int start,int len)
    {
        if (start>s->length+1||start+len>s->length+1)
        {
            printf("不存在该子串!\n");
    	 }
    	String s1,*r;
    	r=&s1;
    	InitString(&s1);
    	int i,j=0;
    	for (i=start-1;i<len;i++)
    	{
    		r->data[j]=s->data[i];
    		r->length++;
    		j++;
    	}
    	return s1;
    } 
    

以上操作不可能利用其他串操作实现;

定位函数Index(S,T)

算法思想:在主串中取从第一个字符起、长度和串T相等的字串,与串T相比较,若相等则求得函数值为i,否则i值增1,直至串S中不存在和串T相等的字串为止。

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

注:

  1. gets(str)与scanf("%s", str)的区别:

    gets(str)允许输入的字符串含有空格;scanf("%s", str)不允许含有空格

串的匹配算法

子串的定位操作通常称为串的模式匹配,它求的是子串(常称模式串)在主串中的位置。

主串S:' s1,s2...sn'

模式串:‘p1,p2...pm'

  1. 简单的模式匹配算法

    这里采用定长顺序结构,给出一种不依赖于其他操作的暴力匹配算法。

    int Index(String S,String T){
        int i = 1,j = 1;
        while(i<=S.length&&j<=T.length){
            if(S.ch[i]==T.ch[j]){
                ++i;++j;	//继续比较后续字符
            }
            else{
                i = i-j+2;j = 1;	//指针后退重新开始匹配
            }
        }
        if(j>T.length)
            return i-T.length;
        else
            return 0;
    }
    

    暴力模式匹配算法的最坏时间复杂度为O(nm),其中n和m分别为主串和模式串的长度。

  2. 改进的模式匹配算法------KMP算法

    ​ 如果已匹配相等的前缀序列中有某个后缀正好是模式的前缀,那么就可以将模式滑动到与这些相等字符对齐的位置,主串i指针无需回溯,并继续从该位置进行比较。而模式向后滑动位数的计算也仅与模式本身的结构有关,与主串无关。

    next函数

    \[next[j]= \begin{cases} 0,&&j=1\\ max\big\{k|1<k<j且'p_1\cdots p_{k-1}'='p_{j-k+1}\cdots p_{j-1}'\big\},&&当此集合不空时\\ 1,&&其他情况 \end{cases} \]

    求next值的程序如下:

    void get_next(String 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;
                next[i] = j;		//若pi=pj,则next[j+1]=next[j]+1
            }
            else
                j = next[j];	//否则令j=next[j],循环继续           
        }
    }
    

    KMP匹配算法:

    int Index_KMP(String S,String 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;
    }
    

    ​ KMP算法的时间复杂度是O(m+n),主要用在主串与子串有很多"部分匹配"时才显得比普通算法快很多,其主要优点是主串不回溯。

  3. KMP算法的进一步优化

    ​ 上述定义的next数组在某些情况下尚有缺陷,还可以进一步优化。若出现了pj=pnext[j],需要再次递归,将next[j]修正为next[next[j]],直至两者不相等为止,更新后的数组命名nextval。计算next数组修正值的算法如下,此时匹配算法不变。

    void get_nextval(String 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];	         
        }
    }
    
posted @ 2022-09-08 18:07  【SmarT】  阅读(308)  评论(0编辑  收藏  举报