字符串基本知识与约定
字符串基础
在阅读本文前请先了解字符,数组这些概念!
一些定义与约定
字符串
字符串(string)是将一些字符按顺序排列而成的一个序列,字符总数称为字符串的长度,在C++中可以用双引号表示字符串。对于一个长度为n的字符串str,我们可以用下标索引str[i]在O(1)的时间内访问其第i+1个字符。对一个长度为n的字符串,我们可以下标索引访问str[i] 的 i 的范围为 0 ~ n-1。
- 字符串str = “abc” ,其中str[0] = ‘a’,str[2]=’c’,长度为3
子串
对于一个长度为n的字符串str,我们由其中第 i , i+1 , … , j-1,j个字符构成的字符串称为str的子串。
我们约定 :str[i ~ j] 和 str[i … j]为 由str[i],str[i+1],… ,str[j-1],str[j]的构成子串。
- 字符串str = “abcdef” 其中str[1…3] = “bcd”,str[4 ~ 5] = “ef”
前后缀
对于一个长度为n的字符串str,其子串str[0 … i]为前缀(子串),str[j … n-1]为后缀(子串)。
一般来说,前/后缀不能等于原字符串。
我们约定:
Pre(str,i) = str[0 … i]
Suff(str , i) = str[n-1-i … n-1]
字典序
类似字典中的排序,我们从小到大地枚举每一个字符,逐位比较(注意是按照ASCII的规则),缺失的位置认为ASCII值为0(注意’0’的ASCII的值不是0而且空格’ ‘的ASCII值为32)。在第一个不同的位上,对应字符的ASCII值大的字符串字典序更大。
- “aa” < “ab”
- “cc” < “cca”
- “00” < “0A” < “0a”
储存方式
一般来说,字符串有以下两种储存形式:char 数组(偶尔char *) 和 std::string(需要头文件string)。前者需要我们预先保留足够的空间(长度不小于字符总数),访问、输入输出较快。而后者会根据输入字符串长度自动申请内存,我们不用考虑其存储问题,且提供了许多的接口(即std::string提供的库函数),方便好用,但输入输出较慢,且所占空间稍多于前者。
char数组
我们一般用 char str[N] 的形式申请,其中输入字符总量不能超过n。因为是数组,所以索引需要从0开始。输入输出如下:
1 | const int N = 114514;//足够大即可 |
为了得到输入的字符串的长度,我们可以使用strlen(str),需要 cstring 库。注意!该操作的时间复杂度为O(n),所以我们一般用一个变量来记录strlen,避免多次调用strlen(str)带来的大量时间开销。
1 |
|
std::string
在输入输出数据量不大的时候,std::string是一个不错的选择,还提供了很多强大的功能,下面将在代码中列举一些常见的操作。
1 |
|
以上是std::string中比较常用的一些函数,感兴趣的同学可以查看C++官方文档中关于std::string的文档来了解更多相关的接口以及具体实现等细节,这里就不多说了。
字符串进阶(个人建议)
聪明的你已经学会了字符串的基础应用,下面我们将学习字符串的一些进阶小知识。由于以下建议局限于答主个人,不保证其高效性,不一定对OI有用,仅供参考。
字符串的储存原理及运用
字符串的储存实际是在原有的字符上增加一个’\0’字符,其ASCII值为0,标志着字符串的终结。此外,读到一般文件的结尾之后,所有读入的字符都变为EOF(end of file),其大小为 -1。因此,读入的字符需要用 int 来传递参数,用来和 char 类型的 255 区分。
知道其储存方式后,我们便可稍加运用,用char数组代替std::string避免低效的输入输出。
用getchar()来读入字符串/数字
getchar()是 iostream下面的一个函数。其可以读入单个字符,返回int类型的ASCII值,适用于需要将字符映射到整数的情况(例如不同的字母对应不同的值)。如果需要存储的char 数组中,需要将末尾字符后一个位置的字符的ASCII值改为0即可。读入过程需要注意些细节:当读入的不在给定字符范围内,一般是空格或者换行,则认为当前字符串读入完毕。同时,每行读入到换行 ‘\n’ 同样要停止。
具体运用 :
- 快速输入输出(快于scanf/printf,cin/cout;模板如下)
1 |
|
用char实现多个字符串读入
很多时候,我们面临着这样一个情景:字符总量巨大,我们明确知道它的上限,有很多组字符串要读入。然而,我们不想用IO(In/Out,指代输入输出)很慢的std::string,甚至有时候字符串组数也未知,用std::vector <std::string>
则必然会占用大量空间。此时,我们应当考虑使用char*数组代替。
具体实现可以用一个int类数组记录第i个字符串的起始位置记作int loc[N],然后用逐个读入字符串,下一个字符串的起始位置为上一个字符串最后一个字符(除了’\0’)的位置+2。
(当然,你也可以直接关闭流同步)
1 |
|