王道学习
4.1 知识框架
4.2 串的定义和实现
字符串简称串,计算机上非数值处理的对象基本都是字符串数据。我们常见的信息检索系统(如搜索引擎)、文本编辑程序(如 Word)、问答系统、自然语言翻译系统等,都是以字符串数据作为处理对象的。本章详细介绍字符串的存储结构及相应的操作。
4.2.1 串的定义
串的逻辑结构和线性表极为相似,区别仅在于串的数据对象限定为字符集。在基本操作上,串和线性表有很大差别。线性表的基本操作主要以单个元素作为操作对象,如查找、插入或删除某个元素等;而串的基本操作通常以子串作为操作对象,如查找、插入或删除一个子串等。
4.2.2 串的基本操作
4.2.3 顺序存储表示
//串--顺序存储--数组
#include <stdio.h>
#define MAXLEN 255
struct SString{
//ch[0]不使用
char ch[MAXLEN];
int length;
};
typedef struct SString SString;
/*
初始化操作
*/
void initString(SString *sString){
(*sString).length=0;
}
/*
注意:数组名作为函数参数,会自动转换为指针
所以,写char chars[]与char *chars,道理相同
所以,需要额外传一个参数,来标识chars的长度
数组名--首地址
赋值操作
chars赋值给sString的ch
返回值
1--成功
0--失败
*/
int strAssign(SString *sString,char *chars,int len){
if(len-1>MAXLEN-1){
return 0;
}
initString(sString);
int i=0;
for(;i<=len-1;i++){
(*sString).ch[i+1]=chars[i];
(*sString).length=(*sString).length+1;
}
return 1;
}
/*
注意:数组名作为函数参数,会自动转换为指针
所以,写char chars[]与char *chars,道理相同
所以,需要额外传一个参数,来标识chars的长度
数组名--首地址
追加操作
chars追加给sString的ch
返回值
1--成功
0--失败
*/
int appendString(SString *sString,char *chars,int len){
if(len-1>MAXLEN-1-(*sString).length){
return 0;
}
int i=0;
for(;i<=len-1;i++){
(*sString).length=(*sString).length+1;
(*sString).ch[(*sString).length]=chars[i];
}
return 1;
}
/*
复制操作
sString的ch复制给t的ch
返回值
1--成功
0--失败
*/
int strCopy(SString *t,SString sString){
int length=sString.length;
int i=1;
for(;i<=length;i++){
(*t).ch[i]=sString.ch[i];
}
return 1;
}
/*
判空操作
返回值
1--非空
0--空
*/
int strEmpty(SString sString){
if(sString.length>0){
return 1;
}else{
return 0;
}
}
/*
求串长
*/
int strLength(SString sString){
return sString.length;
}
/*
清空操作
*/
void clearString(SString *sString){
(*sString).length=0;
}
/*
销毁串操作--main运行结束,弹栈--自动销毁
*/
void destoryString(SString *sString){
(*sString).length=0;
}
/*
拼接操作
str1的ch与str2的ch拼接赋值给t的ch
返回值
1--成功
0--失败
*/
int concat(SString *t,SString s1,SString s2){
int len1=strLength(s1);
int len2=strLength(s2);
if(len1+len2>MAXLEN-1){
return 0;
}
initString(t);
int i=1;
for(;i<=len1;i++){
(*t).ch[i]=s1.ch[i];
(*t).length=(*t).length+1;
}
int j=1;
for(;j<=len2;j++){
(*t).length=(*t).length+1;
(*t).ch[(*t).length]=s2.ch[j];
}
return 1;
}
/*
求子串
s的ch截取赋值给sub的ch
返回值
1--成功
0--失败
*/
int subString(SString *sub,SString s,int pos,int len){
//子串范围越界
if(pos+len-1>strLength(s)){
return 0;
}
int i=pos;
for(;i<pos+len;i++){
(*sub).ch[i-pos+1]=s.ch[i];
}
(*sub).length=len;
return 1;
}
/*
字符串比较--s的ch与t的ch比较
返回值
正数--s>t
0--相等
负数--s<t
*/
int strCompare(SString s,SString t){
int len1=strLength(s);
int len2=strLength(t);
int i=1;
for(;i<=len1&&i<=len2;i++){
if(s.ch[i]!=t.ch[i]){
return s.ch[i]-t.ch[i];
}
}
return len1-len2;
}
/*
定位操作
若主串s中存在于串t值相同的子串,
则返回主串s中第一次出现的位置
否则函数值为0
*/
int index(SString s,SString t){
int i=1;
int n=strLength(s);
int m=strLength(t);
SString sub;
while(i<=n-m+1){
subString(&sub,s,i,m);
if(strCompare(s,sub)!=0){
i++;
}else{
return i;
}
}
return 0;
}
/*
输出串
返回值
1--成功
0--失败
*/
void printfSString(SString sString){
int length=strLength(sString);
if(length==0){
return 0;
}
printf("\n");
int i=1;
for(;i<=length;i++){
printf("%c",sString.ch[i]);
}
//%s会输出ch[0]
//printf("%s",sString.ch);
return 1;
}
int main(){
SString sString;
char str1[]="123";
char str2[]="abc";
initString(&sString);
printf("%d\n",strLength(sString));
printfSString(sString);
return 0;
}
#include <stdio.h>
#define MAXLEN 255
struct SString{
char ch[MAXLEN];
int length;
};
typedef struct SString SString;
void initString(SString *sString){
sString->length=0;
}
int strAssign(SString *sString,char *chars,int len){
if(len-1>MAXLEN-1){
return 0;
}
initString(sString);
int i=0;
for(;i<=len-1;i++){
sString->ch[i+1]=chars[i];
sString->length=sString->length+1;
}
return 1;
}
int appendString(SString *sString,char *chars,int len){
if(len-1>MAXLEN-1-sString->length){
return 0;
}
int i=0;
for(;i<=len-1;i++){
sString->length=sString->length+1;
sString->ch[sString->length]=chars[i];
}
return 1;
}
int strCopy(SString *t,SString sString){
int length=sString.length;
int i=1;
for(;i<=length;i++){
t->ch[i]=sString.ch[i];
}
return 1;
}
int strEmpty(SString sString){
if(sString.length>0){
return 1;
}else{
return 0;
}
}
int strLength(SString sString){
return sString.length;
}
void clearString(SString *sString){
sString->length=0;
}
void destoryString(SString *sString){
sString->length=0;
}
int concat(SString *t,SString s1,SString s2){
int len1=strLength(s1);
int len2=strLength(s2);
if(len1+len2>MAXLEN-1){
return 0;
}
initString(t);
int i=1;
for(;i<len1;i++){
t->ch[i]=s1.ch[i];
t->length=t->length+1;
}
int j=1;
for(;i<=len2;j++){
t->length=t->length+1;
t->ch[t->length]=s2.ch[j];
}
return 1;
}
int subString(SString *sub,SString s,int pos,int len){
if(pos+len-1>strLength(s)){
return 0;
}
int i=pos;
for(;i<pos+len;i++){
sub->ch[i-pos+1]=s.ch[i];
}
sub->length=len;
return 1;
}
int strCompare(SString s,SString t){
int len1=strLength(s);
int len2=strLength(t);
int i=1;
for(;i<=len1&&i<=len2;i++){
if(s.ch[i]!=t.ch[i]){
return s.ch[i]-t.ch[i];
}
}
return len1-len2;
}
int index(SString s,SString t){
int i=1;
int n=strLength(s);
int m=strLength(t);
SString sub;
while(i<=n-m+1){
subString(&sub,s,i,m);
if(strCompare(s,sub)!=0){
i++;
}else{
return i;
}
}
return 0;
}
void printfSString(SString sString){
int length=strLength(sString);
if(length==0){
return 0;
}
printf("\n");
int i=1;
for(;i<=length;i++){
printf("%c",sString.ch[i]);
}
return 1;
}
int main(){
SString sString;
char str1[]="123";
char str2[]="abc";
initString(&sString);
printf("%d\n",strLength(sString));
printfSString(sString);
return 0;
}
4.2.4 链式存储表示
4.3 串的模式匹配
4.3.1 简单的模式匹配算法
//串--顺序存储--数组
#include <stdio.h>
#define MAXLEN 255
struct SString{
//ch[0]不使用
char ch[MAXLEN];
int length;
};
typedef struct SString SString;
/*
初始化操作
*/
void initString(SString *sString){
(*sString).length=0;
}
/*
注意:数组名作为函数参数,会自动转换为指针
所以,写char chars[]与char *chars,道理相同
所以,需要额外传一个参数,来标识chars的长度
数组名--首地址
赋值操作
chars赋值给sString的ch
返回值
1--成功
0--失败
*/
int strAssign(SString *sString,char *chars,int len){
if(len-1>MAXLEN-1){
return 0;
}
initString(sString);
int i=0;
for(;i<=len-1;i++){
(*sString).ch[i+1]=chars[i];
(*sString).length=(*sString).length+1;
}
return 1;
}
/*
求串长
*/
int strLength(SString sString){
return sString.length;
}
/*
定位操作
若主串s中存在于串t值相同的子串,
则返回主串s中第一次出现的位置
否则函数值为0
*/
int indexFirst(SString s,SString t){
int k=1;
int i=k,j=1;
while(i<=strLength(s)&&j<=strLength(t)){
if(s.ch[i]==t.ch[j]){
i++;
j++;
}else{
k++;
i=k;
j=1;
}
}
if(j>strLength(t)){
return k;
}else{
return 0;
}
}
/*
定位操作
若主串s中存在于串t值相同的子串,
则返回主串s中第一次出现的位置
否则函数值为0
*/
int indexSecond(SString s,SString t){
int i=1,j=1;
while(i<=strLength(s)&&j<=strLength(t)){
if(s.ch[i]==t.ch[j]){
i++;
j++;
}else{
i=i-j+2;
j=1;
}
}
if(j>strLength(t)){
return i-strLength(t);
}else{
return 0;
}
}
/*
输出串
返回值
1--成功
0--失败
*/
void printfSString(SString sString){
int length=strLength(sString);
if(length==0){
return 0;
}
printf("\n");
int i=1;
for(;i<=length;i++){
printf("%c",sString.ch[i]);
}
//%s会输出ch[0]
//printf("%s",sString.ch);
return 1;
}
int main(){
SString sString;
char str1[]="123";
char str2[]="abc";
initString(&sString);
printf("%d\n",strLength(sString));
printfSString(sString);
return 0;
}
#include <stdio.h>
#define MAXLEN 255
struct SString{
char ch[MAXLEN];
int length;
};
typedef struct SString SString;
void initString(SString *sString){
sString->length=0;
}
int strAssign(SString *sString,char *chars,int len){
if(len-1>MAXLEN-1){
return 0;
}
initString(sString);
int i=0;
for(;i<=len-1;i++){
sString->ch[i+1]=chars[i];
sString->length=sString->length+1;
}
return 1;
}
int strLength(SString sString){
return sString.length;
}
int indexFirst(SString s,SString t){
int k=1;
int i=k,j=1;
while(i<=strLength(s)&&j<=strLength(t)){
if(s.ch[i]==t.ch[j]){
i++;
j++;
}else{
k++;
i=k;
j=1;
}
}
if(j>strLength(t)){
return k;
}else{
return 0;
}
}
int indexSecond(SString s,SString t){
int i=1,j=1;
while(i<=strLength(s)&&j<=strLength(t)){
if(s.ch[i]==t.ch[j]){
i++;
j++;
}else{
i=i-j+2;
j=1;
}
}
if(j>strLength(t)){
return i=strLength(t);
}else{
return 0;
}
}
void printfSString(SString sString){
int length=strLength(sString);
if(length==0){
return 0;
}
printf("\n");
int i=1;
for(;i<=length;i++){
printf("%c",sString.ch[i]);
}
//%s会输出ch[0]
//printf("%s",sString.ch);
return 1;
}
int main(){
SString sString;
char str1[]="123";
char str2[]="abc";
initString(&sString);
printf("%d\n",strLength(sString));
printfSString(sString);
return 0;
}
4.3.2 改进的模式匹配算法——KMP算法
1、字符串的前缀、后缀和部分匹配值
要了解子串的结构,首先要弄清楚几个概念:前缀、后缀和部分匹配值。前缀指除最后一个字符以外,字符串的所有头部子串;后缀指除第一个字符外,字符串的所有尾部子串;部分匹配值则为字符串的前缀和后缀的最长相等前后缀长度。下面以’ ababa '为例进行说明:
2、KMP算法介绍
由D.E.Knuth,J.H.Morris和V.R.Pratt提出,因此称为KMP算法
3、求next数组
4、KMP算法性能分析
4.3.2 KMP算法的进一步优化
4.3.3 本节试题精选