串(字符串)
一、定义
串,即字符串,是由零个或多个字符组成的有限序列,一般记为S = ‘a1 a2 a3 … an’,S为串名,单引号内的字符序列是串的值,ai可以是字母、数字、其他字符,串中字符的个数n称为串的长度,n = 0为空串。 例如:S = “hello world!”
子串:串的子序列,空串必是子串;
主串:包含子串的串;
字符在主串的位置:字符在串中的序号;
子串在主串的位置:子串的第一个字符在主串的位置;
空串:没有一个字符的串;
空格串:只包含空格的串,一个空格字符占1B,空串不等于空格串;
串是一种特殊的线性表,数据之间呈现线性关系
串的数据对象限定为字符集(中文字符、英文字符、数字字符、标点字符等)
二、基本操作
增删改查通常以子串作为操作对象
1.赋值操作:
StrAssign(&T,chars):把串T赋值为chars
2.复制操作:
StrCopy(&T,S):将串S复制得到串T
3.判空:
StrEmpty(S):若S为空串,则返回true,否则返回false
4.求串长:
StrLength(S):返回串S的元素个数
5.清空:
ClearString(&S):将S清空为空串
6.销毁:
DestroyString(&S):将串S销毁,回收存储空间
7.串联接:
Concat(&T,S1,S2):用T返回由S1和S2连接而成的新串
8.求子串:
SubString(&Sub,S,pos,len):用Sub返回串S的第pos个字符起,长度为len的子串
9.定位:
Index(S,T):若主串S与串T值相同的子串,则返回它在主串S中第一次出现的位置,否则值为0
10.比较:
StrCompare(S,T):若S>T,则返回值>0;若S=T,则返回值=0;若S<T,则返回值<0
比较方法:
从第一个字母开始向后依次对比,先出现更大的字母,则此字符大;若相同子串的长串与短串相比,长串更大。
三、串的存储结构
(一)顺序存储:
ch[0]废弃不用,从ch[1]开始
//顺序存储声明一个串(静态存储)
#define MAXLEN 255//预定最大串长为255
typedef struct{
char ch[MAXLEN];//每个分量存储一个字符
int length;//串的实际长度
}SString;
//顺序存储声明一个串(动态分配),需要malloc函数分配空间
typedef struct{
char* ch;//按串长分配存储区,ch指向串的基地址
int length;
}HString;
(二)链式存储:
/链式存储声明一个串
typedef struct StringNode0{
char ch;//每个结点存一个字符
struct StringNode0* next;
}StringNode0,*String0;
typedef struct StringNode1{
char ch[4];//每个结点存多个字符
struct StringNode1* next;
}StringNode1,*String1;
(三)基于顺序存实现的基本操作:
1.串的声明:
#include <stdio.h>
#include <stdlib.h>
//顺序存储声明一个串(静态存储)
#define MAXLEN 255
typedef struct{
char ch[MAXLEN];
int length;
}SString;
//顺序存储声明一个串(动态分配)
typedef struct{
char* ch;
int length;
}HString;
//链式存储声明一个串
typedef struct StringNode0{
char ch;
struct StringNode0* next;
}StringNode0,*String0;
typedef struct StringNode1{
char ch[4];
struct StringNode1* next;
}StringNode1,*String1;
2.求子串:
#include <stdio.h>
//顺序存储声明一个串(静态存储)
#define MAXLEN 255
typedef struct{
char ch[MAXLEN];
int length;
}SString;
//求子串
bool SubString(SString &Sub,SString S,int pos,int len){
if(pos+len-1 > S.length){
return false;
}
for(int i = pos;i < pos+len;i ++){
Sub.ch[i-pos+1] = S.ch[i];//ch[i-pos+1]:当i=pos时,为ch[i],因为舍弃了ch[0]
}
Sub.length = len;
return true;
}
int main(){
return 0;
}
3.比较操作:
int StrCompare(SString S,SString 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]; /返回负数或0或正数
}
}
}
4.定位操作
int Index(SString S,SString T){
SString sub;//暂存子串
int n = StrLength(S);
int m = StrLength(T);
int i = 1;
while(i < n-m+1){
SubString(sub,S,i,m);
if(StrCompare(sub,T) != 0){
++i;
}else{
return i;
}
}
return 0;
}
5.代码集合:
#include <stdio.h>
//顺序存储声明一个串(静态存储)
#define MAXLEN 255
typedef struct{
char ch[MAXLEN];
int length;
}SString;
//求子串
bool SubString(SString &Sub,SString S,int pos,int len){
if(pos+len-1 > S.length){
return false;
}
for(int i = pos;i < pos+len;i ++){
Sub.ch[i-pos+1] = S.ch[i];//ch[i-pos+1]:当i=pos时,为ch[i],因为舍弃了ch[0]
}
Sub.length = len;
return true;
}
//比较
int StrCompare(SString S,SString 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];
}
}
}
//求串长
int StrLength(SString S){
int i = 1;
while(i < S.length){
i ++;
}
return i;
}
//定位
int Index(SString S,SString T){
SString sub;//暂存子串
int n = StrLength(S);
int m = StrLength(T);
int i = 1;
while(i < n-m+1){
SubString(sub,S,i,m);
if(StrCompare(sub,T) != 0){
++i;
}else{
return i;
}
}
return 0;
}
int main(){
return 0;
}
四、字符串模式匹配
(一)字符串的朴素模式匹配算法
1.概念:
在字符串中搜索需要的内容
2.基本术语:
主串:需要查询的文本
模式串:需要查的内容
字符串模式匹配:在主串中找到与模式串相同的子串,并返回其位置
注意:子串是主串的一部分,但模式串不一定是主串的一部分(匹配不了)
3.实现思想:
暴力方式:依次解决,若模式串长度为6,则在主串中找出所有长度为6的子串,依次和模式串匹配;
在长度为n的主串中,有n-m+1个长度为m的子串,定位操作采用的就是此算法:
int Index(SString S,SString T){
SString sub;//暂存子串
int n = StrLength(S);
int m = StrLength(T);
int i = 1;
while(i < n-m+1){
SubString(sub,S,i,m);
if(StrCompare(sub,T) != 0){
++i;
}else{
return i;
}
}
return 0;
}
通过数组下标实现朴素模式匹配算法:
int Index1(SString S,SString T){
int i = 1;
int j = 1;
while(i <= S.length && j <= T.length){
if(S.ch[i] == T.ch[j]){//相等则继续扫描
++i;
++j;
}else{//不等则主串返回到下一个子串,模式串返回到初始位置
i = i-j+2;
j = 1;
}
}
if(j > T.length){//模式串指针指向位置大于模式串的长度,即已找到匹配
return i-T.length; //主串中已匹配子串的第一个元素的位置
}else{
return 0;//所有子串匹配失败
}
}
最好时间复杂度:O(n)
最坏时间复杂度:O(nm)
(二)KMP算法:
1.概念:
朴素模式匹配算法,当发现当前子串最后一个字符不匹配,只能转而匹配下一个子串,需要从头开始,但是,当前子串的前几个字符一定是和模式串相匹配的,即可知,若依次移动子串位置时,已匹配的字符不需要再参与匹配(必定不匹配),例如T = ‘abaabc’:
当第6个元素失配:
当第5个元素失配:
当第4个元素失配:
当第3个元素失配:
当第2个元素失配:
当第1个元素失配:
总体而言:
2.C语言实现:
//KMP算法:用数组实现:
int Index_KMP(SString S,SString T,int next[]){//模式匹配部分
int i = 1;
int j = 1;
while(i <= S.length && j <= T.length){
if(j == 0 || S.ch[i] == T.ch[j]){
++i;
++j;
}else{
j = next[j];//模式串向右移动
}
}
if(j > T.length){
return i-T.length;//匹配成功
}else{
return 0;
}
}
最坏时间复杂度:O(n+m)
3.手动求next数组:
例题一:
模式串相右后移依次,对比除了已经未匹配的其他字符,有一个不匹配则继续右移模式串
例题二(详细过程):
第三个匹配,则在第二个后面划线,模式串右移一位,但a和b无法匹配,则继续右移
第四个匹配,则在第三个后面划线,模式串后移一位,模式串第一个元素a和子串第二个元素b不一样,则模式串继续右移一位,模式串第一个a元素和子串第三个元素a相同,其后在没有位于线左边的模式串元素,则next为2
第五个匹配,则在第四个后面划线,模式串后移一位,模式串第一个元素a和子串第二个元素b不同,则模式串继续右移一位,模式串第一个元素a和子串第三个元素a匹配,模式串第二个元素b和子串第四个元素b相同,则next为3
例题三: