串
前言
自己学习数据结构的笔记,看的是王道的视频,总结了串的基本概念,代码实现,以及朴素模式匹配算法和KMP算法。写了能跑的源代码,有详细的注释。希望对大家学习数据结构有所帮助。
一、串的基本概念
1.串: 零个或多个字符组成的有限序列串名
2.串的长度:串中字符的个数n
3.空串:n=0时的串;空格串:由一个或多个空格组成的串
4.子串:串中任意多个连续的字符组成的子序列称为该串的子串
5.主串:包含子串的串
6.字符在主串中的位置:某个字符在串中的序号(从1开始)
7.子串在主串中的位置:子串的第一个字符在主串中的位置
8.串是特殊的线性表,数据元素之间呈线性关系(逻辑结构相似)
二、串的基本操作
1.定义和初始化
//顺序存储定义
#define MAXLEN 255
typedef struct{
char ch[MAXLEN];
int length;
}SString;
//链式存储定义
typedef struct{
char *ch;
int length;
}HString;
//初始化串
void InitString(HString &S){
S.ch = (char *)malloc(MAXLEN*sizeof(char));
S.length = 0;
}
//串的判空
bool StrEmpty(HString S){
if(S.length == 0)
return true;
return false;
}
2.求串长
//求串长
int StrLength(HString S){
return S.length;
}
3.比较
//比较两个串的大小
int StrCompare(HString S,HString 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;
}
4.复制
//串的复制
bool StrCopy(HString &T,HString S){
T = S;
return true;
}
5.拼接
//字符串的拼接
bool Concat(HString &T,HString S1,HString S2){
int i, j;
for(i=0; i<S1.length; i++){
T.ch[i] = S1.ch[i];
}
T.length = S1.length;
for(j=0; j<S2.length; j++){
T.ch[T.length] = S2.ch[j];
T.length++;
}
return true;
}
6.定位
//定位操作
int Index(HString S,HString T){
int i = 1;
n = strLength(S);
m = strLength(T);
HString sub; //用于暂存子串
InitString(sub);
while(i<n-m+1){
SubString(sub,S,i,m);
if(StrCompare(Sub,T) != 0)
i++;
else
return i;
}
//S中不存在与T相等的字串
return 0;
}
7.求子串
//求子串
bool SubString(HString &Sub,HString S,int pos,int len){
//1.子串范围越界
if(pos+len-1 > S.length)
return false;
//2.求子串
for(int i = 1;i <= len;i++){
Sub.ch[i] = S.ch[pos+i-1];
}
Sub.ch[len+1] = '\0';
Sub.length = len;
return true;
}
三、串的朴素模式匹配算法
//串的朴素模式匹配算法
int Index_BF(HString S,HString T){
int i = 0,j = 0;
while(i<S.length && j<T.length){
if(S.ch[i] == T.ch[j]){
i++;
j++;
}else{
i = i-j+1;
j = 0;
}
}
if(j == T.length)
return i-T.length+1;
else
return -1;
}
主串长度为n,模式串长度为m
最好时间复杂度 = O(n)
最坏时间复杂度 = O(nm)
四、KMP算法
//求模式串T的next数组
void get_next(HString 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算法
int Index_KMP(HString S,HString T){
int i = 0,j = 0;
int next[T.length+1];
get_next(T,next);
//next数组优化
int nextval[MAXLEN];
nextval[1] = 0;
for(int j=2;j<=T.length;j++){
if(T.ch[next[j]] == T.ch[j])
nextval[j] = nextval[next[j]];
else
nextval[j] = next[j];
}
while(i<S.length && j<T.length){
if(j==0 || S.ch[i] == T.ch[j]){
i++;
j++;
}else{
j = nextval[j];
}
}
if(j == T.length)
return i - T.length + 1;
else
return -1;
}
求next数组时间复杂度 = O(m)
模式匹配过程最坏时间复杂度 = O(n)
KMP算法的最坏时间复杂度 = O(m+n)
五、源代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXLEN 255
//静态数组存储
typedef struct{
char ch[MAXLEN];
int length;
}SString;
//动态存储
typedef struct{
char *ch;
int length;
}HString;
//函数声明
void InitString(HString &S); //初始化
bool SubString(HString &Sub,SString S,int pos,int len); //求子串
bool StrCopy(HString &T,HString S); //复制,将串S复制到T
bool StrAssign(HString &T,char* chars); //赋值
bool StrEmpty(HString S); //判空
bool Concat(HString &T,HString S1,HString S2); //拼接两个串
void get_next(HString T,int next[]); //求模式串的next数组
int StrLength(HString S); //求串长
int StrCompare(HString S,HString T); //比较两个串的大小
int Index(HString S,HString T); //定位
int Index_BF(HString S,HString T); //朴素模式匹配算法
int Index_KMP(HString S,HString T); //KMP算法
//主函数
int main(void) {
HString S,T,S1,S2,S3;
InitString(S);
InitString(T);
InitString(S1);
InitString(S2);
InitString(S3);
char s[] ="abcdef";
char t[] ="cde";
char s1[] ="0";
char s2[] ="1234";
char s3[] ="abc";
StrAssign(S1,s1);
StrAssign(S2,s2);
StrAssign(S3,s3);
StrAssign(S,s);
StrAssign(T,t);
printf("S串:%s 串长为:%d\n",S.ch,StrLength(S));
printf("T串:%s 串长为:%d\n",T.ch,StrLength(T));
printf("S1串:%s 串长为:%d\n",S1.ch,StrLength(S1));
printf("S2串:%s 串长为:%d\n",S2.ch,StrLength(S2));
printf("S3串:%s 串长为:%d\n",S3.ch,StrLength(S3));
printf("==================\n");
Concat(S1,S2,S3);
printf("将串S2和S3拼接后新串为:%s 串长为:%d\n",S1.ch,StrLength(S1));
printf("串s2比串s3大%d\n",StrCompare(S2,S3));
StrCopy(S1,S2);
printf("串S1复制为S2后串为:%s 串长为:%d\n",S1.ch,StrLength(S1));
printf("T串在S串中的位置为:%d\n",Index(S,T));
printf("朴素模式匹配算法求T串在S串的位置为:%d\n",Index_BF(S,T));
printf("KMP算法求T串在S串的位置为:%d\n",Index_KMP(S,T));
return 0;
}
//初始化串
void InitString(HString &S){
S.ch = (char *)malloc(MAXLEN*sizeof(char));
S.length = 0;
}
//求串长
int StrLength(HString S){
return S.length;
}
//串的赋值
bool StrAssign(HString &T,char *chars){
int i;
int len = 0;
//c指针指向字符串首地址
char *c = chars;
//当c指向空,也就是遍历完chars时,退出循环
while(*c != NULL){
len++;
//c的值加1,也就是地址值加一
c++;
}
//如果传入字符串为空,则将T置为空
if(len == 0){
T.ch = NULL;
T.length = 0;
return true;
} else{
//多分配一个空间给结束符
T.ch=(char*)malloc(sizeof(char)*(len+1));
if(T.ch == NULL)
return false;
else{
c=chars;
//给T赋值
for(i=0;i<=len;i++,c++){
T.ch[i]=*c;//c是所指向的地址值,*c是指所向地址的值
}
T.length = len;
return true;
}
}
}
//串的复制,将串S复制为T
bool StrCopy(HString &T,HString S){
T = S;
return true;
}
//串的判空
bool StrEmpty(HString S){
if(S.length == 0)
return true;
return false;
}
//求子串
bool SubString(HString &Sub,HString S,int pos,int len){
//1.子串范围越界
if(pos+len-1 > S.length)
return false;
//2.求子串
for(int i = 1;i <= len;i++){
Sub.ch[i] = S.ch[pos+i-1];
}
Sub.ch[len+1] = '\0';
Sub.length = len;
return true;
}
//比较两个串的大小
int StrCompare(HString S,HString 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(HString S,HString T){
int i = 1;
int n = StrLength(S);
int m = StrLength(T);
HString sub; //用于暂存子串
InitString(sub);
while(i<n-m+1){
SubString(sub,S,i,m);
if(StrCompare(T,sub) != 0)
i++;
else
return i;
}
//S中不存在与T相等的子串
return 0;
}
//字符串的拼接
bool Concat(HString &T,HString S1,HString S2){
int i, j;
for(i=0; i<S1.length; i++){
T.ch[i] = S1.ch[i];
}
T.length = S1.length;
for(j=0; j<S2.length; j++){
T.ch[T.length] = S2.ch[j];
T.length++;
}
return true;
}
//串的朴素模式匹配算法
int Index_BF(HString S,HString T){
int i = 0,j = 0; //用i扫描主串S,j扫描模式串T
while(i<S.length && j<T.length){
if(S.ch[i] == T.ch[j]){
i++;
j++;
}else{
i = i-j+1;
j = 0;
}
}
if(j == T.length)
return i-T.length+1;
else
return -1;
}
//求模式串T的next数组
void get_next(HString 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算法
int Index_KMP(HString S,HString T){
int i = 0,j = 0; //用i扫描主串S,j扫描模式串T
int next[T.length+1];
get_next(T,next);
//next数组优化
int nextval[MAXLEN];
nextval[1] = 0;
for(int j=2;j<=T.length;j++){
if(T.ch[next[j]] == T.ch[j])
nextval[j] = nextval[next[j]];
else
nextval[j] = next[j];
}
while(i<S.length && j<T.length){
if(j==0 || S.ch[i] == T.ch[j]){
i++;
j++;
}else{
j = nextval[j];
}
}
if(j == T.length){
return i - T.length + 1;
}
else{
return -1;
}
}