本篇博客主要记录我最近所学的有关数据结构与算法中字符串的知识。
目录
1、串的定义
一、基本概念
•串:由零个或多个字符串组成的有限序列。一般记为:S=“a1a2...an”,其中ai为ASCII码字符。
•术语:串名、串值、串的长度、空串(Ø)、空格串、子串、主串、字符的位置(1~n)、子串的位置、串的相等。
• 串与线性表的区别:串是一种特殊的线性表。数据元素为字符,且一次可操作若干个数据元素(子串)。
二、串的抽象数据类型的定义:
(1) StrAssign (&T, chars) //串赋值
(2) StrCompare (S, T) //串比较,返回>0、0、<0
(3) StrLength (S) //求串长
(4) Concat(&T, S1, S2) //串联
(5) SubString(&Sub, S, pos, len) //求子串
(6) StrCopy(&T, S) //串拷贝
(7) StrEmpty(S) //串判空
(8) ClearString (&S) //清空串
(9) Index(S, T, pos) //子串的位置(S中第pos位置后)
(10) Replace(&S, T, V) //串替换(可多次)
(11) StrInsert(&S, pos, T) //子串插入
(12) StrDelete(&S, pos, len) //子串删除
(13) DestroyString(&S) //串销毁
二、C语言中有关串的函数<string.h>:
实际应用中,为实现串操作,即可使用高级语言提 供的串函数,也可自己重新设计。
C语言用字符数组存储串,串末尾自动添加‘\0’
2、串的表示与实现
一、串的顺序存储结构表示和实现:
数组有静态(固定)数组和动态数组两种,因此,串的顺序存储结构也相应的有两种。
1、静态数组存储结构(定长数组结构)
typedef struct { char ch[MAXSTRLEN+1]; //从ch[1]开始存储 int length; //串的当前长度(书上用ch[0]存放长度) } SString; SString s; for (i=1; i<=4; i++) s.ch[i]=‘A’+i; s.length=4;
2、常用的字符串函数的应用
函数strlen(str1)求串长,s为所求串的地址
#include<stdio.h>
#include<string.h>
int main(){
int ans;
char str1[] ="I am a student";
ans = strlen(str1);
printf("%d",ans);
return 0;
}
函数strcpy(str1, str2) 拷贝,将字符串s复制到t中,返回字符串t,s也可为字符串
#include<stdio.h>
#include<string.h>
int main(){
char str1[] ="I am a student";
char str2[] ="Hello world";
strcpy(str1+2,str2);
puts(str1);
return 0;
}//此时打印出来的内容为I Hello world,此时是从str1的第二个位置进行复制
#include<stdio.h>
#include<string.h>
int main(){
char str1[] ="I am a student";
char str2[] ="Hello\0world";
strcpy(str1,str2);
puts(str1);
return 0;
}//此时打印出来的内容为Hello,\0也会被复制过去
追加函数 strcat(str1,str2)
#include<stdio.h>
#include<string.h>
int main(){
char str1[] ="I am a student ";
char str2[] ="Hello world";
strcat(str1,str2);
puts(str1);
return 0;
}//库函数中strcat函数的声明是
char * strcat ( char * destination, const char * source );
//此时打印出来的为I am a student Hello world,将str2追加在str1后面
注意:
1.追加的字符串str2和目标字符串str1中都必须要带有字符’\0’,并且追加字符串str2必须以‘\0’结尾,否则追加过程无法顺利实现。
2.目标字符串空间必须足够大(足够容纳下追加字符串str2的内容)。
3.目标空间必须可修改(前面不能加const并且不能说常量字符串)
字符串比较函数strcmp(str1,str2)
#include<stdio.h>
#include<string.h>
int main(){
int ans;
char str1[] ="I am a student ";
char str2[] ="Hello world";
ans = strcmp(str1,str2);
if(ans>0)
printf(">");
else if(ans<0)
printf("<");
else
printf("=");return 0;
}//strcmp函数比较的本质是比较每一个字符的ASCII码值,比较过程中字符ASCII码值大的,则它所属的字符串大,返回的是>
3、串的模式匹配算法
算法目的:
确定主串中所含子串(模式)第一次出现的位置 (定位),即实现Index(S, T, pos)函数
算法种类:
•BF算法(Brute Force)(又称古典的、经典的、朴素 的、穷举的)
•KMP算法(Kruth, Morris, Pratt)(特点:速度快)
BF算法
Index(S,T,pos)
• 将主串的第pos个字符和模式的第一个字符比较, 若相等,继续逐个比较后续字符; 若不等,从主串的下一字符起,重新与模式的第 一个字符比较。
• 直到主串的一个连续子串字符序列与模式相等 。 返回值为S中与T匹配的子序列第一个字符的序号, 即匹配成功。
• 否则,匹配失败,返回值
算法设计描述:
typedef struct{
int length;
char *ch;
}Sstring;
int Index(Sstring S,Sstring T,int pos){
i = pos;
j = 1;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]){
j++;
i++;
}
else{
i = i-j+2;
j=1;
}
}
if ( j>T.length) return i-T.length;
else return 0;
}
BF算法时间复杂度
若n为主串长度,m为子串长度,最坏情况是:为O(n*m),如下图所示:
KMP算法
算法设计思想
利用已经部分匹配的结果而加快模式串的滑动速度, 且主串S的指针i不必回溯!可提速到O(n+m)!
建立辅助数组next
next数组为前缀表,数组内容代表相同前后缀的长度
void kmp(int next[], char s[]){
int i=1,j=0;
next[0] = 0;
for(;i<strlen(s);i++){
while(j>0&&s[j]!=s[i]){
j = next[j-1]; //如果s[i]!=s[j],则j进行回退,回退到下标为next[j-1]的位置
}
if(s[j]==s[i]){
j++; //如果s[i]==s[j],则将j加1,并赋值给next数组中i的位置
}
next[i]=j;}
}
KMP算法
int strStr(char haystack[], char needle[]){ //haystack为主串,needle为字串
int i=0,j=0;
int next[strlen(needle)]; //定义字串的前缀表
kmp(next,needle);
for(;i<strlen(haystack);i++){
while(j>0&&needle[j]!=haystack[i]){
j = next[j-1];
}
if(haystack[i]==needle[j]){
j++;
}
if(j==strlen(needle)){
return (i-j+1);}
}
return -1;
}
leetcode28. 实现 strStr()是kmp算法的经典题目,大家有兴趣的可以去挑战一下
4、总结
第一次写博客,感觉写的挺久的,但是内容并不多,而且还很散乱,因为也是最近才开始深入接触计算机这门学科,懂的并不是很多,希望能够坚持下去,在这里总结自己所刷过的题目,课堂上的内容。
不管全世界所有人怎么说,我都认为自己的感受才是正确的。无论别人怎么看,我绝不打乱自己的节奏。喜欢的事自然可以坚持,不喜欢怎么也长久不了。———村上春树