串
字符串的简称, 字符串本身就是一种数据结构, 由零个或者多个字符组成的有限序列(顺序存储结构)
串的定义及子串
定义 :在C语言中, 串可以用字符数组来定义:
char str[] = "adrui";
这里
str
是串的名字, 串长为5
,而str数组存储的字符分别为'a'
,'d'
,'r'
,'u'
,'i'
,'\0'
其中
'\0'
是编译器识别串结束的标志, 说以str串长为5, str数组长度为6子串: 串中任意连续字符组成的序列称为该串的
子串
, 包含子串的串称为主串
, 主串的某一子串在主串中的位置为该子串第一个字符在主串中的位置(保证从这个位置开始能和子串匹配)
空白字符
\n
, '\r'
, '\t'
, ' '
等空白字符也可以作为串的元素
特点
- 可以看成是限定了元素类型为字符的线性表
- 操作集上主要针对串内的一个子串
C语言字符数组和字符型指针
字符数组
char str[] = "adrui"; /**静态分配, 在内存中开辟一块区域, 存储'a', 'd', 'r', 'u', 'i'和'\0' 串长为5,数组长为6**/ char s[100]; //静态分配, 在内存中开辟一区域, 数组长度为100, 串长不定(自己可赋值要 以'\0'结尾)
字符数组开辟后, 地址和长度均不可变, 不加
const
修饰符时可修改某一位置的元素, 加const
之后不可修改字符指针
char * str1 = "adrui";//str1指向内存中某一块区域,该区域存着该字符串常量(不可修改) char str[] = "adrui"; str1 = str; //可修改(str本身可修改) const char * str2;//指向的区域元素不可修改, 但是本身可修改指向 char * const str3;//本身指向不可修改, 指向区域的元素可以修改
注: 修饰符定义可修改, 还要看指向的指针或者数组是否支持元素修改
const char *
且不能赋给char *
,"adrui"
这种字符串常量本身也是不可改的
串定义
定长顺序存储表示及操作集:
typedef struct{ char ch[MAX_SIZE + 1];//最大容量为MAX_SIZE, 多出来的1用来存'\0' int len; //串长 }Str; int strLength(const char *p){ //获取串长 int len = 0; while(*p){ //串尾为'\0', 其整数值为0, 到达串尾会退出循环 ++p; ++len; } return len; } void strAssign(Str &str, const char *p){ //赋值 int length = strLength(p); for(int i = 0; i <= length; ++i) //赋值[0, len],p[len]为'\0' str.ch[i] = p[i]; str.len = length; } int StrLength(Str str){ return str.len; }
变长分配存储表示及操作集:
typedef struct{ char *ch; int len; }Str; int strLength(const char *p){ //获取串长 int len = 0; while(*p){ ++p; ++len; } return len; } void strAssign(Str &str, const char *p){ //赋值 int length = strLength(p); if(length == 0) { //空串 free(str.ch); str.ch == NULL; } else { str.ch = (char *)malloc((length + 1) * sizeof(char)); //开辟空间 for(int i = 0; i <= length; ++i) //赋值[0, len],p[len]为'\0' str.ch[i] = p[i]; } str.len = length; } int StrLength(Str str){ return str.len; } int strCompare(Str s1, Str s2){ //按字典序比较 for(int i = 0; i < s1.len && i < s2.len; ++i) if(s1.ch[i] != s2.ch[i]) return s1.ch[i] - s2.ch[i]; return s1.len - s2.len; } int index(Str str, Str substr){ int len1 = StrLength(str); int len2 = StrLength(substr); for(int i = 0; i <= len1 - len2; ++i){ int j, k = i; for(j = 0; j < len2 && str.ch[k] == substr.ch[j]; ++k, ++j); if(j == len2) return i; } return -1; } void strPrint(Str str){ if(str.len == 0) { printf("\n"); return; } printf("%s\n", str.ch); }
串的模式匹配算法
串的模式匹配: 对一个串中某子串的定位, 其中待定位的子串称为模式串
暴力枚举
int index(Str str, Str substr){ int len1 = StrLength(str); int len2 = StrLength(substr); for(int i = 0; i <= len1 - len2; ++i){ int j, k = i; for(j = 0; j < len2 && str.ch[k] == substr.ch[j]; ++k, ++j); if(j == len2) return i; } return -1; }
KMP匹配算法
有关Kmp算法的原理这里就不讲了, 转载一篇博文
int nxt[MAX_SIZE + 1];//index from 1 void getNext(Str substr){ nxt[1] = 0; int i = 1, j = 0; while(i <= StrLength(substr)){ if(j == 0 || substr.ch[i] == substr.ch[j]){ ++i; ++j; nxt[i] = j; }else j = nxt[j]; } } int kmp(Str str, Str substr){ int i = 1, j = 1; while(i <= StrLength(str) && j <= StrLength(substr)){ if(j == 0 || str.ch[i] == substr.ch[j]){ ++i; ++j; }else j = nxt[j]; } if(j > StrLength(substr)) return i - StrLength(str); return -1; }
改进KMP算法:
int nxt[MAX_SIZE + 1];//index from 1 void getNext(Str substr){ nxt[1] = 0; int i = 1, j = 0; while(i <= StrLength(substr)){ if(j == 0 || substr.ch[i] == substr.ch[j]){ ++i; ++j; nxt[i] = substr.ch[i] == substr.ch[j] ? nxt[j] : j; //优化, 失配指针的移动 }else j = nxt[j]; } } int kmp(Str str, Str substr){ int i = 1, j = 1; while(i <= StrLength(str) && j <= StrLength(substr)){ if(j == 0 || str.ch[i] == substr.ch[j]){ ++i; ++j; }else j = nxt[j]; } if(j > StrLength(substr)) return i - StrLength(str); return -1; }
AC自动机
多个主串的模式串匹配算法, 以KMP算法和字典树为基础(有兴趣可以学习一下)