数据结构_C语言_串_串的置换操作Replace (&S, T, V)的算法、字符串反序的递推或递归算法,例如字符串为“abcsxw”,反序为“wxscba”、串的模式匹配算法及改进KMP算法

前言


串的定义:串(字符串)是由零个或多个字符组成的有限序列。

对于串的基本操作集可以有不同的定义方式,对于串类型的最小操作子集有

  • 串赋值
  • 串比较
  • 求串长
  • 串联接
  • 求子串

是最基本的操作子集,其他串操作都可以在这些操作上实现

串的表示和实现

串有3种机内表示方法

  • 顺序存储结构

    • 定长顺序存储表示(按照预定义的大小开辟一段连续的存储单元)
    • 堆分配存储表示(也是地址连续的一段存储空间,只不过是根据使用需求动态分配的)
  • 链式存储结构

    • 串的块链存储表示(多个字符共用一个节点,密度大)

目录

  • 串的定长存储实现
  • 串的堆分配表示
  • 一些串操作的算法
    • 串的置换
    • 串的递归反序
    • 串的模式匹配算法
    • 串的模式匹配算法改进版KMP

开始

一、串的定长存储实现

直接上代码,很容易理解

需要注意的地方:

  • SString是一种自定义的结构类型,其中能存放unsigned char 类型的元素
  • 规定一般字符数组的s[0] 存放字符串的长度lemgth
  • 至于为什么是255,因为无符号数的范围为 0 - 255,其实底层存储的还是ASCII码(一串01的代码),归根结底还是字节,万物皆字节
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;


//串的定长顺序存储表示

//char 在c语言中站1个字节,2^8 - 1 = 255 
#define MAXSTRLEN 255

//规定字符数组的s[0]存放子符串的长度,这其实也就是字符串底层实现的原理
//自定义数据类型 SString 
typedef unsigned char SString[MAXSTRLEN + 1] ;


 
int main(){
	SString s = "abcde";
	cout<<s<<endl;
	
	cout<<"=================" <<endl<<endl;
	
	 
	SString s1;
	cout<<"未赋初值的时候,存放的默认为:"<<s1[0]<<endl<<endl;
	
	cout<<"=================" <<endl<<endl; 
	

	 //当然这里可以不进行长度的输入,直接s[0]就存放有效字符
	cout<<"请输入这个新的字符串(第一位为字符串的长度):"<<endl;
	cin>>s1; 
	
	cout<<"这个字符串为:";
	int i = 1;
	while(s1[i] != NULL){
		cout<<s1[i];
		i++;
	} 
	cout<<endl;
	
	cout<<"s1[0]可以设置有效字符串的长度:" <<s1[0]<<endl<<endl;
	
} 

在这里插入图片描述

二、串的堆分配表示

这种存储结构的串操作,仍是 基于 “字符序列的复制”进行的

#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;

//串的堆分配表示

typedef struct{
	char *ch;//若是非空串,则按串长分配存储区,否则ch为NULL 
	int length;//串长度 
}HString;

int main(){
	
	HString s;
	cout<<"请输入字符串的长度"<<endl;
	cin>>s.length;
	s.ch = (char*)malloc(s.length * sizeof(char));
	cout<<"请输入字符串:"<<endl;
	cin>>s.ch;
	
	cout<<endl;
	cout<<"您输入的字符串为:"<<s.ch<<"长度为:"<<s.length<<endl;
} 

在这里插入图片描述

三、一些串操作的算法

3.1串的置换
3.1.1涉及:
  • 串的堆分配表示
  • 字符串模式匹配(这里使用最普通的算法,下面会使用KMP等改进后的算法)
  • 字符串替换(也是普通的3个for来解决)
3.1.2程序运行截图

在这里插入图片描述
在这里插入图片描述

3.1.3代码
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
using namespace std;

//串的堆分配表示

typedef struct{
	char *ch;//若是非空串,则按串长分配存储区,否则ch为NULL 
	int length;//串长度 
}HString;

//1. 实现串的初始化
void InitStr(HString &s) ;

//2. 一般的串模式匹配 ,子串定位 
int Index(HString s,HString T,int pos);

//3. 实现串的置换
void Replace(HString &s,HString &new_s,HString T, HString V,int index);
 

int main(){
	

	//创建第一个串 
	HString s;
	InitStr(s);
	//创建第二个串,作为匹配子串
	cout<<"请输入需要进行匹配的的字符串s1" <<endl;
	HString s1;
	InitStr(s1);
	//创建第三个串,如果匹配成功,作为替换串 
	
	//模式匹配,如果s1为s的子串,就进行接下来的操作,否则就提示匹配失败
	 
	cout<<"请输入s1从什么位置开始匹配s:( 1 <=N<="<< s.length<<")"<<endl;
	int pos;
	cin>>pos;
	int index = Index(s,s1,pos);
	
	//判断是否成功匹配 
	if(index) {
		cout<<"子串s1 在 s 的位置为 :"<< index<<endl<<endl;
		cout<<"请输入要替换的目标串s2" <<endl;
		HString s2;
		InitStr(s2);
		
		
		HString new_s;
		
		
		
		Replace(s,new_s,s1,s2,index);
		for(int i = 0;i<new_s.length;i++){
			cout<<new_s.ch[i];
		}
		 
		
		
	}else{
		cout<<"字符串 s1 不是 s 的字串!" <<endl; 
	}
	

	return 0;	
} 

//1. 实现串的初始化
void InitStr(HString &s){
	
		
		cout<<"请输入字符串的长度"<<endl;
		cin>>s.length;
		s.ch = (char*)malloc(s.length * sizeof(char));
		cout<<"请输入字符串:"<<endl;
		cin>>s.ch;

		cout<<"您输入的字符串为:"<<s.ch<<"长度为:"<<s.length<<"初始化完成!"<<endl;		
		cout<<endl;
}

//2. 一般的串模式匹配 ,子串定位 
int Index(HString s,HString T,int pos){
	
	int i = pos;
	int j = 1;
	while(i<= s.length && j<=T.length){
		if(s.ch[i - 1] == T.ch[j - 1]){
			++i;
			++j;
		}
		else{
			i = i - j + 2;
			j = 1; 
		}
	}
	if(j>T.length){
		return i - T.length;
	}
	else
		return 0;
}

// //3. 实现串的置换
void Replace(HString &s,HString &new_s,HString T, HString V,int index){
	
	new_s.length = s.length + (V.length - T.length);
	new_s.ch = (char*)malloc(s.length * sizeof(char));
	
	//字串之前的不动 
	int i;
	for(i = 0;i< index - 1;i++){
		new_s.ch[i] = s.ch[i];
	}
	//替换的目标串插入 
	for(int j = index - 1;j<V.length + i + 1;j++){
		new_s.ch[j] = V.ch[j - index + 1];
	}
	//字串之后的不动 
	for(int k = i + V.length ;k<new_s.length;k++){
		new_s.ch[k] = s.ch[k - i + V.length - T.length ] ;
	}
	 
	
	
	
}

3.2串的递归反序

之前记录了一篇Java版的 连续逐个输入若干字符,以 #号结束,实现结束时 输出 输入字符的逆序

就是递归实现,目前实现不太符合字符串整体性,后续有时间会优化

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;



char DiGui(){
	char ch;
	cout<<"请输入字符串的每个字符:(一个字符一次回车,以#为结束的标志)"<<endl;
	cin>>ch;
	
	if(ch == '#'){
		return NULL;
	}
	else{
		DiGui();
		cout<<ch<<" ";
	}
}

 
int main(){
	
	
	
	
	DiGui();
	return 0;
	
} 



在这里插入图片描述

3.3串的模式匹配算法

普通模式匹配算法 实现见上面的 3.1 已经基本实现

部分代码

//2. 一般的串模式匹配 ,子串定位 
int Index(HString s,HString T,int pos){
	
	int i = pos;
	int j = 1;
	while(i<= s.length && j<=T.length){
		if(s.ch[i - 1] == T.ch[j - 1]){
			++i;
			++j;
		}
		else{
			i = i - j + 2;
			j = 1; 
		}
	}
	if(j>T.length){
		//正常找到结束,还有 ++j ,返回查找到的位置
		return i - T.length;
	}
	else
		return 0;
}

注意这里我采用的是 堆顺序分配的存储结构 实现的串

3.4串的模式匹配算法改进版KMP

在这里插入图片描述

普通查询算法和KMP查找算法 写在一个程序里面,可以查询两次

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;

//串定长顺序存储表示
#define MAXSTRLEN 255

//串可以存放不止字母,还可以特殊字符 
typedef unsigned char SString[MAXSTRLEN + 1];

//1. 字符串初始化函数 
void InitString(SString &s); 

//2. 普通模式匹配算法
int index(SString s1,SString s2,int pos);

//3. KMP模式匹配
int indexKMP(SString s1,SString s2,int pos);
void get_next(SString s2,int next[]);

int next[20];


int main(){
	
	SString s1;
	InitString(s1); 
	
	SString s2;
	InitString(s2);
	
	cout<<endl<<"=============="<<endl; 
	
	int pos1;
	cout<<"请输入你要开始查找的位置:"<<endl;
	cin>>pos1;
	int findIndex = index(s1,s2,pos1);
	if(findIndex > 0){
		cout<<"s2是s1的子串!所在的位置为: "<<findIndex<<endl;
	}
	else{
		cout<<"s2不是s1的子串 !"<<endl; 
	}
	
	int pos2;
	cout<<"请输入你要开始查找的位置:"<<endl;
	cin>>pos2;
	int findIndex2 = index(s1,s2,pos2);
	if(findIndex2 > 0){
		cout<<"s2是s1的子串!所在的位置为: "<<findIndex2<<endl;
	}
	else{
		cout<<"s2不是s1的子串 !"<<endl; 
	}
	
	get_next(s2,next);
	indexKMP(s1,s2,pos2);
	
	 
	
	
	 
}

//1. 字符串初始化函数 
void InitString(SString &s){
	cout<<"请输入字符串 :(首位表示字符串的长度)"<<endl;
	cin>>s;
	cout<<"字符串初始化完成!";
	cout<<"字符串的长度为 :"<<s[0]<<"字符串的内容为:";
	int i = 1;
	while(s[i] != '\0'){
		cout<<s[i];
		i++;
	}  
	
	cout<<endl<<endl;
}

//2. 普通模式匹配算法
int index(SString s1,SString s2,int pos){
	int i = pos;
	int j = 1;
	
	//这里需要进行ascii码的转换,s[0]存放的长度为字符 型的,需要转化为整形 
	while(i<= int(s1[0] ) - 48 && j<= int(s2[0]) - 48){
		if(s1[i] == s2[j]){
			i++;
			j++;
		}
		else{
			//出现不相等的,就从s1的第 i - j + 2的位置开始重新比较 
			//目标子串从j = 1的位置开始比较 
			i = i - j + 2;
			j = 1;
		}
	}
	
	//目标子串全部找到 
	// 字符0的ascii码为48 
	if(j > (int(s2[0]) - 48) ){
		
		return i - int(s2[0]) + 48;
	} 
	else{
		/*
		
		cout<<"debug"<<endl;
		cout<<i<<endl;
		cout<<int(s2[0])<<endl;
		*/
		
		return 0;
	}
}

//3. KMP模式匹配
int indexKMP(SString s1,SString s2,int pos){
	
	int i = pos;
	int j = 1;
	while(i<=int(s1[0]) - 48 && j<=int(s2[0]) - 48){
		if(j == 0 || s1[i] == s2[j]){
			i++;
			j++;
		}
		else{
			j = next[j];
		}
	}
}

void get_next(SString s2,int next[]){
	int i = 1;
	next[1] = 0;
	int j = 0;
	while(i < int(s2[0]) - 48){
		if(j == 0 || s2[i] == s2[j]){
			j++;
			i++;
			next[i] = j;
		}
		else{
			j = next[j];
		}
	}
}