C语言中如何用一个字符串替换一个主串中的子串

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言中如何用一个字符串替换一个主串中的子串相关的知识,希望对你有一定的参考价值。

参考技术A 参考以下代码
#include
<string.h>
//oldstr原字符串,
newstr新字符串,
oldsubstr原字符串中要替换的
子串
,
newsubstr新字符串中替换后的
新子

void
replace(char
*oldstr,
char
*newstr,
char
*oldsubstr,
char
*newsubstr)

int
i,
j;
int
nLen
=
strlen(oldstr);
int
nLenSub
=
strlen(oldsubstr);
for(i=0,
j=0;
i<nLen;)

if(0
==
strncmp(oldstr+i,
oldsubstr,
nLenSub))

strcat(newstr+j,
newsubstr);
j
+=
strlen(newsubstr);
i
+=
nLenSub;

else

newstr[j++]
=
oldstr[i++];


数据结构-串数组和广义表

串、数组和广义表

串的定义

串(String)——零个或多个任意字符组成的有限序列

  • 子串:串中任意个连续字符组成的子序列称为该串的子串

    真子串是指不包含自身的所有子串

  • 主串:包含子串的串相应地称为主串

  • 字符位置:字符在序列中的序号为该字符在串中的位置

  • 子串位置子串第一个字符在主串中的位置

  • 空格串:由一个或多个空格组成的串,与空串不同

串相等:当且仅当两个串的长度相等并且各个对应位置上的字符都相同时,这两个串才是相等的。

  • 所有空串是相等的

串的类型定义、存储结构及运算

ADT String
    数据对象:D=a_i|a_i∈CharacterSet,i=1,2,...,n,n≥0
    数据关系:R_1=<a_i-1,a_i>|a_i-1,a_i∈D,i=1,2,...,n
    基本操作:
        StrAssign(&T,chars)	//串赋值
        StrCompara(S,T)		//串比较
        StrLength(S)		//求串长
        Concat(&T,S1,S2)	//串连结
        SubString(&Sub,S,pos,len)	//求子串
        StrCopy(&T,S)		//串拷贝
        StrEmpty(S)			//串判空
        ClearString(&S)		//清空串
        Index(S,T,pos)		//子串的位置
        Replace(&S,T,V)		//串替换
        StrInsert(&S,pos,T)	//子串插入
        StrDelete(&S,pos,len)		//子串删除
        DestroyString(&S)	//串销毁
ADT String
  • 串的顺序存储结构

    #define MAXLEN 255
    typedef struct
        char ch[MAXLEN+1];	//存储串的一维数组
        int length;			//串的当前长度
    SString;
    
  • 串的链式存储结构——块链结构

    优点:操作方便 缺点:存储密度较低(存储密度=串值所占的存储/实际分配的存储)

    可将多个字符存放在一个结点中,以克服其缺点

    #define CHUNKSIZE 80	//块的大小可由用户定义
    typedef struct Chunk
        char ch[CHUNKSIZE];
        struct Chunk *next;
    Chunk;
    
    typedef struct
        Chunk *head,*tail;	//串的头指针和尾指针
        int curlen;			//串的当前长度
    LString;				//字符串的块链结构
    
  • 串的模式匹配算法

    算法目的:

    ​ 确定主串中所含**子串(模式串)**第一次出现的位置(定位)

    算法应用:

    ​ 搜索引擎、拼写检查、语言翻译、数据压缩

    算法种类:

    • BF算法(Brute-Force,又称古典的、经典的、朴素的、穷举的)

      简单匹配算法,采用穷举法的思路,从S的每一个字符开始依次与T的字符进行匹配

      int Index_BF(SString S,SString T)
          int i=1,j=1;
          while(i<=S.length&&j<=T.length)
              if(s.ch[i]==t.ch[j])++i;++j;//主串和子串依次匹配下一个字符
              elsei=i-j+2;j=1;//主串、子串指针回溯重新开始下一次匹配
          
          if(j>=T.length) return i-T.length;//返回匹配的第一个字符的下标
          else return 0;//模式匹配不成功
      
      

      若n为主串长度,m为子串长度,最坏情况是总次数为:(n-m)*m+n=(b-m+1) * m,若m<<n,则算法复杂度O(n * m)

    • KMP算法(特点:速度快)

      利用已经部分匹配的结果而加快模式串的滑动速度?

      且主串S的指针i不必回溯!可提速到O(n+m)

      为此,定义**next[j]**函数,表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置。

      n e x t [ j ] = m a x k ∣ 1 < k < j , 且 " p 1 . . . p k − 1 " ( 从头开始的 k − 1 个元素 ) = " p j − k + 1 . . . p j − 1 " ( j 前面的 k − 1 个元素 ) 当此集合非空时 0 当 j = 1 时 1 其他情况 next[j]= \\begincases max\\k|1<k<j,且"p_1...p_k-1"(从头开始的k-1个元素)="p_j-k+1...p_j-1"(j前面的k-1个元素)\\\\\\ \\quad \\quad \\quad \\quad 当此集合非空时\\\\ 0\\quad 当j=1时\\\\ 1\\quad 其他情况 \\endcases next[j]= maxk∣1<k<j,"p1...pk1"(从头开始的k1个元素)="pjk+1...pj1"(j前面的k1个元素)当此集合非空时0j=11其他情况

      int Index KMP(SString S,SString T,int pos)
          i=pos;j=1;
          while(i<S.length&&j<T.length)
              if(j==0||S.ch[i]==T.ch[j])i++;j++;
              else
                  j=next[j];/*i不变,j后退*/
          
          if(j>T.length) return i-T.length;/*匹配成功*/
          else return 0;/*返回不匹配标志*/
      
      
      void get_next(SString T,int &next[])
          i=1;next[1]=0;j=0;
          while(i<T.length)
              if(j==0|T.ch[i]==T.ch[j])
                  ++i;++j;
                  next[i]=j;
              
              else
                  j=next[j];
          
      
      //next改进
      void get_nextval(SString T,int &nextval[])
          i=1;nextval[1]=0;j=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];
          
      
      

数组

数组:按一定格式排列起来的、具有相同类型的数据元素的集合。

一维数组:若线性表中的数据元素为非结构的简单元素,则称为一维数组。

一维数组的逻辑结构线性结构。定长的线性表。

声明格式:数据类型 变量名称[长度];

二维数组:若一维数组中的数据元素又是一维数组结构,则称为二维数组。

二维数组的逻辑结构

  • 非线性结构

    每一个数据元素既在一个行表中,又在一个列表中。

  • 线性结构、定长的线性表

    该线性表的每个数据元素也是一个定长的线性表。

声明格式:数据类型 变量名称[行数] [列数];

在C语言中,一个二维数组类型也可以定义为一维数组类型(其分量类型为一维数组类型),即:

typedef elemtype array2[m][n];
//等价于
typedef elemtype array1[n];
typedef array1 array2[m];

三维数组:若二维数组中的元素又是一个一维数组,则称作三维数组。

n维数组:若n-1维数组中的元素又是一个一维数组结构,则称作n维数组。

结论:线性表结构是数组结构的一个特例,而数组结构又是线性表结构的扩展。

数组特点:结构固定——定义后,维数和维界不再改变。

数组基本操作:除了结构的初始化和销毁之外,只有取元素和修改元素值的操作。

特殊矩阵的压缩存储

矩阵:一个由mxn个元素排成的m行n列的表。

矩阵的常规存储:将矩阵描述为一个二维数组。

矩阵的常规存储的特点:可以对其元素进行随机存取;矩阵运算非常简单;存储的密度为1。

不适宜常规存储的矩阵:值相同的元素很多且呈某种规律分布;零元素多。

矩阵的压缩存储:为多个相同的非零元素只分配一个存储空间;对零元素不分配空间。

  1. 对称矩阵

    特点:在n×n的矩阵a中,满足如下性质:aij=aji(1≤i,j≤n)

    存储方法:只存储下(或者上)三角(包括主对角线)的数据元素。共占用n(n+1)/2个元素空间。

  2. 三角矩阵

    特点:对角线以下(或者以上)的数据元素(不包括对角线)全部为常数c。

    存储方法:重复元素c共享一个元素存储空间,共占用n(n+1)/2+1个元素空间:sa[1…n(n+1)/2+1]
    存储下标 上三角矩阵: k = ( i − 1 ) × ( 2 n − i + 2 ) / 2 + j − i + 1 i ≤ j n ( n + 1 ) / 2 + 1 i > j 下三角矩阵: k = i × ( i − 1 ) / 2 + j i ≥ j n ( n + 1 ) / 2 + 1 i < j 存储下标\\\\ 上三角矩阵:k= \\begincases (i-1)×(2n-i+2)/2+j-i+1\\quad i≤j \\\\ n(n+1)/2+1\\quad i>j \\endcases\\\\ 下三角矩阵:k= \\begincases i×(i-1)/2+j\\quad i≥j \\\\ n(n+1)/2+1\\quad i<j \\endcases 存储下标上三角矩阵:k=(i1)×(2ni+2)/2+ji+1ijn(n+1)/2+1i>j下三角矩阵:k=i×(i1)/2+jijn(n+1)/2+1i<j

  3. 对角矩阵(带状矩阵)

    特点:在n×n的方阵中,所有非零元素都集中在以主对角线为中心的带状区域中,区域外的值全为0,则称为对角矩阵。常见的有三对角矩阵、五对角矩阵、七对角矩阵等。

    存储方法:以对角线的顺序存储


稀疏矩阵:设在m×n的矩阵中有t个非零元素。令δ=t/(m×n),当δ≤0.05时称为稀疏矩阵

压缩存储原则:存各非零元的值、行列位置和矩阵的行列数。

注意:为更可靠描述,通常再加一个“总体”信息:即总行数、总列数、非零元素总个数

三元组顺序表又称有序的双下标法

三元组顺序表的优点:非零元在表中按行有序存储,因此便于进行依行顺序处理的矩阵运算

三元组顺序表的缺点:不能随机存取。若按行号存取某一行中的非零元,则需从头开始进行查找。

稀疏矩阵的链式存储结构:十字链表

优点:它能够灵活地插入因运算而产生的新的非零元素,删除因运算而产生的新的零元素,实现矩阵的各种运算。

在十字链表中,矩阵的每一个非零元素用一个结点表示,该结点除了(row,col,value)以外,还要有两个域:

  • right:用于链接同一行中的下一个非零元素;
  • down:用以链接同一列中的下一个非零元素。

广义表

广义表(又称列表Lists)是n≥0个元素a0,a1,…,a_n-1的有限序列,其中每一个ai或者是原子,或者是一个广义表

广义表通常记作:LS=(a1,a2,…,an) 其中:LS为表名,n为表的长度,每一个ai为表的元素

习惯上,一般用大写字母表示广义表小写字母表示原子

表头:若LS非空(n≥1),则其第一个元素a1就是表头。记作head(LS)=a1。 :表头可以是原子,也可以是子表。

表尾:除表头之外的其它元素组成的。记作tail(LS)=(a2,…,an)。 :表尾不是最后一个元素,而是一个子表。

广义表的性质

  1. 广义表中的数据元素有相对次序一个直接前驱和一个直接后继

  2. 广义表的长度定义为最外层所包含元素的个数

  3. 广义表的深度定义为该广义表展开后所含括号的重数

    A=(b,c)的深度为1,B=(A,d)的深度为2,C=(f,B,h)的深度为3

    注意:“原子”的深度为0;“空表”的深度为1

  4. 广义表可以为其他广义表共享;如:广义表B就共享表A。在B中不必列出A的值,而是通过名称来引用,B=(A)

  5. 广义表可以是一个递归的表。如:F=(a,F)

    注意:递归表的深度是无穷值,长度是有限值。

  6. 广义表是多层次结构,广义表的元素可以是单元素,也可以是子表,而子表的元素还可以是子表,…。

广义表可以看成是线性表的推广,线性表是广义表的特例

广义表的结构相当灵活,在某种前提下,它可以兼容线性表、数组、树和有向图等各种常用的数据结构。

二维数组的每行(或每列)作为子表处理时,二维数组即为一个广义表。

广义表的基本运算

  1. 求表头GetHead(L):非空广义表的第一个元素,可以是一个单一的元素,也可以是一个子表
  2. 求表尾GetTail(L):非空广义表除去表头元素以外其它元素所构成的表。表尾一定是一个表

以上是关于C语言中如何用一个字符串替换一个主串中的子串的主要内容,如果未能解决你的问题,请参考以下文章

数据结构-串数组和广义表

字符串模糊匹配

字符串

(原创)数据结构之利用KMP算法解决串的模式匹配问题

数据结构(十五)串的顺序存储结构(顺序串)

C语言课程设计题目计算字符串中子串出现的次数