字符串

介绍

本节内容将介绍在 Python 里处理字符串的基本概念,以及正则表达式的使用,以及介绍几种常见的字符串处理算法。

知识点

  • 字符串的基本概念
  • 正则表达式
  • KMP 算法
  • 编辑距离
  • manacher 算法

基本概念

字符串是 Python 中最常见的数据类型。我们可以使用引号 (' 或 ")来创建字符串。

创建字符串很简单,只要为变量分配一个值即可。例如:

var1='Hello shiyanlou'

Python 访问字符串中的值使用方括号来截取字符串,如下示例:

var2='Hello shiyanlou!'
print("var2[1:5]:",var2[1:5])

Python 字符串更新

可以截取字符串的一部分并与其他字段拼接

var3='Hello user!'
print("已更新字符串:",var3[:6],'user!')

执行结果: 已更新字符串:Hello user!

Python 转义字符

在需要在字符中使用特殊字符时,Python 用反斜杠(\) 转义字符。

转义字符 描述
\(在行尾时) 续行符
\' 单引号
\‘’ 双引号
\b 退格(Backspace)
\t 横向制表符

Python 字符串运算符

操作符 描述
+ 字符串连接
* 重复输出字符串
[] 通过索引获取字符串中字符
[:] 截取字符串中的一部分,遵循左闭右开原则,str[0,2] 是不包含第 3 个字符的
in 如果字符串中包含给定的字符返回 True
not in 如果字符串中不包含给定的字符返回 True

Python 字符串格式化

Python 支持格式化字符串的输出。尽管这样可能会用到非常复杂的表达式,但最基本的用法是将一个值插入到一个有字符串格式符 %s 的字符串中

示例如下:

print("Hello user %d,Welcome to %s"%(34291615,"shiyanlou"))

输出结果:Hello user 34291615,Welcome to shiyanlou

Python 字符串格式化符号

符号 描述
%c 格式化字符及其 ASCII 码
%s 格式化字符串
%d 格式化整数
%e 用科学计数法格式化浮点数

正则表达式

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。re 模块使 Python 语言拥有全部的正则表达式功能。

re.match 函数

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回 None

函数语法:

re.match(pattern,string,flags=0)

函数参数说明:

参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

实例:

import re
print(re.match('www', 'www.shiyanlou.com').span())  # 在起始位置匹配
print(re.match('com', 'www.shiyanlou.com'))         # 不在起始位置匹配

以上实例运行输出结果为:

(0,3)
None

re.search 方法

re.search 扫描整个字符串并返回第一个成功的匹配

函数语法:

re.search(pattern,string,flags=0)

函数参数说明:

参数 描述
pattern 要匹配的正则表达式
string 要匹配的字符串
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等

实例:

import re
print(re.search('www', 'www.shiyanlou.com').span())  # 在起始位置匹配
print(re.search('com', 'www.shiyanlou.com').span())   # 不在起始位置匹配

以上实例运行输出结果为:

(0,3)
(11,14)

re.match 与 re.search 的区别

re.match 只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None;而 re.search 匹配整个字符串,直到找到一个匹配

检索和替换

Python 的 re 模块提供了 re.sub 用于替换字符串中的匹配项。

语法:

re.sub(pattern,repl,string,count=0,flags=0)
  • pattern:正则表达式中模式字符串
  • repl:替换的字符串,也可为一个函数
  • string:要被查找替换的原始字符串
  • count:模式匹配后替换的最大次数,默认 0 表示替换所有的匹配
import re
phone="2004-959-559 # 这是一个国外电话号码"

#删除字符串中的 Python注释
num=re.sub(r'#.*$',"",phone) #r代表正则表达式,将在后面介绍具体规则
print("电话号码是:",num)

# 删除非数字(-)的字符串 
num = re.sub(r'\D', "", phone)
print("电话号码是:",num)

执行结果如下:
电话号码是: 2004-959-559
电话号码是 : 200495955

recompile 函数

compile 函数用于编译正则表达式,生成一个正则表达(Pattern)对象,供 match() 和 search() 这两个函数使用。

语法格式为:

re.compile(pattern[,flags])

参数:

  • pattern: 一个字符串形式的正则表达式
  • flags:可选,表示匹配模式,比如忽略大小写,多行模式,具体参数为:
    • re.l 忽略大小写
    • re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境
    • re.M 多行模式
    • re.S . 并且包括换行符在内的任意字符(. 不包括换行符)
    • re.X 为了增加可读性,忽略空格和 # 后面的注释

findall

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

语法:

findall(string[,pos[,endpos]])

参数:

  • string:待匹配的字符串
  • pos:可选参数,指定字符串的起始位置,默认为 0。
  • endpos:可选参数,指定字符串的结束位置,默认为字符串的长度。

查找字符串中的所有数字:

import re
pattern=re.compile(r'\d+') #查找数字
result1 = pattern.findall('runoob 123 google 456')
result2 = pattern.findall('run88oob123google456', 0, 10)
print(result1)
print(result2)

输出结果:
['123','456']
['88','12']

re.split

split 方法按照能够匹配的子串将字符串分割后返回列表

语法如下:

re.split(pattern, string[, maxsplit=0, flags=0])

参数:

参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串
maxsplit 分隔次数,maxsplit =1 分隔一次,默认为 0,不限制次数
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等

正则表达式实例

字符类

实例 描述
rub[ye] 匹配 "ruby" 或 "rube"
[aeiou] 匹配中括号内的任意一个字母
[0-9] 匹配任何数字。类似于 [0123456789]
[a-z] 匹配任何小写字母
[A-Z] 匹配任何大写字母
[a-zA-Z0-9] 匹配任何字母及数字
[^aeious] 除了 aeiou 字母以外的所有字符
[^0-9] 匹配除了数字外的字符

特殊字符类

实例 描述
\d 匹配一个数字字符。等价于 [0-9]
\D 匹配一个非数字字符。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]
\S 匹配任何非空白字符。
\w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'
\W 匹配任何非单词字符。

KMP 算法

字符串匹配是极为常见的一种模式匹配。简单地说,就是判断主串 T 中是否出现模式串 P,即 P 为 T 的子串。特别地,定义主串 T[0...n-1],模式串为 P [0...p-1],则主串与模式串的长度各为 n 与 p

暴力匹配

暴力匹配方法的思想非常朴素:

  1. 依次从主串的首字符开始,与模式串逐一进行匹配
  2. 遇到失配时,则移到主串的第二个字符,将其与模式串首字符比较,逐一进行匹配
  3. 重复上述步骤,直至能匹配上,或剩下主串的长度不足以进行匹配

参考代码如下:

def brute_force_match(t,p):
    tlen=len(t)
    plen=len(p)
    for i in range(tlen):
        j=0
        while t[i+j]==p[j] and j <plen:
            j=j+1
            if j==plen:
                return i
    return -1

算法复杂度分析:i 在主串上移动 (n-p)次,匹配失败时,j 移动次数最多有 p-1 次。因此复杂度为 O(n*p)

对于暴力匹配而言,就存在重复匹配的现象。比如,第一次匹配失败时,主串,模式串失败匹配的位置的字符分别为 a 与 c ,下一次匹配时主串,模式串的起始位置分别为 T1 与 P[0];而在模式串中 c 之前是 ab ,未有重复结构,因此 T1与 P[0]肯定不能匹配上,这样造成了重复匹配。直观上,下一次匹配应从 T2 与 P[0] 开始。

那么如何提高匹配效率呢?那就考虑下 KMP 算法

KMP 算法原理

KMP 详细证明

KMP 算法思想归纳如下:将主串 T 的第一个字符与模式串 P 的第一个字符进行匹配。如果相等,则依次比较 T 和 P 的下一个字符。如果不相等,则 主串 T 移动(已匹配字符数-对应的部分匹配值)位,继续匹配。

关于移动位数的解释:已匹配字符数,即当前已完成匹配的字符数量。部分匹配值就是前缀和后缀的最长的共有元素的长度。

前缀指除了最后一个字符以外,一个字符串的全部头部组合

后缀指除了第一个字符以外,一个字符串的全部尾部组合

举个例子:以"ABCDABD"为例

  • "A"的前缀和后缀都为空集,共有元素的长度为 0;
  • "AB"的前缀为[A],后缀为[B],共有元素的长度为 0;
  • "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度 0;
  • "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为 0;
  • "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为 1;
  • "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为 2;
  • "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为 0。

部分匹配表如下:

搜索值 A B C D A B D
部分匹配值 0 0 0 0 1 2 0

作业

请根据 KMP 算法原理,编写 kmp 算法实现字符匹配吧。

主串:123456456

目标串:123456

/home/shiyanlou/下新建一个文件kmp.py

参考代码

注意:请务必自己独立思考解决问题之后再对照参考答案,一开始直接看参考答案收获不大。

参考代码如下:

def get_failure_array(pattern): #制作部分匹配表
    failure = [0]
    i = 0
    j = 1
    while j < len(pattern):
        if pattern[i] == pattern[j]:
            i += 1
        elif i > 0:
            i = failure[i-1]
            continue
        j += 1
        failure.append(i)
    return failure
def kmp(pattern, text):
    failure = get_failure_array(pattern) #得到部分匹配表
    i=0
    j=0
    while i < len(text):
        if pattern[j] == text[i]:
            if j == (len(pattern) - 1):
                return True
            j += 1
        elif j > 0:
            j = failure[j - 1]
            continue
        i += 1
    return False
if __name__=="__main__":
    print(kmp('123456','123123456'))

编辑距离

编辑距离(levenshtein distance)是针对二个字符串(例如英文字)的差异程度的量化量测,量测方式是看至少需要多少次的处理才能将一个字符串变成另一个字符串。编辑距离可以用在自然语言处理中,例如拼写检查可以根据一个拼错的字和其他正确的字的编辑距离,判断哪一个(或哪几个)是比较可能的字。DNA 也可以视为用 A、C、G 和 T 组成的字符串,因此编辑距离也用在生物信息学中,判断二个 DNA 的类似程度。Unix 下的 diff 及 patch 即是利用编辑距离来进行文本编辑对比的例子。

注:定义来自百度百科

算法原理

编辑距离允许的编辑操作有三种,如果字符相等,我们还可以选择不操作。我们可以列举一下这四种操作的情况。

  1. 不操作:对于"ay"与"day"的 "y"字符,可以选择不操作。即"ay"与"day"之间的利文斯顿距离等于"a"和"da"之间的利文斯顿距离。

  2. 替换操作:对于"day"与"dae"的最后一位字符"y"和"e"字符,可以选择进行替换操作。即"day"与"dae"之间的利文斯顿距离等于"da"和"da"之间的利文斯顿距离加上一。

  3. 插入操作:对于"dae"与"da"的最后一位字符"e"和"a"字符,可以选择进行插入操作。即"dae"与"da"之间的利文斯顿距离等于"da"和"da"之间的利文斯顿距离加上一。

  4. 删除操作:对于"dae"与"daey"的最后一位字符"e"和"a"字符,可以选择进行删除操作。即"dae"与"daey"之间的利文斯顿距离等于"dae"和"dae"之间的利文斯顿距离加上一。

作业

请根据 levenshtein 算法原理,编写 levenshtein 函数实现比较两个字符串的编辑距离。

例子如下:

>>> levenshtein_distance("planet", "planetary")
3
>>> levenshtein_distance("", "test")
4
>>> levenshtein_distance("book", "back")
2
>>> levenshtein_distance("book", "book")
0
>>> levenshtein_distance("test", "")
4
>>> levenshtein_distance("", "")
0
>>> levenshtein_distance("orchestration", "container")
10

/home/shiyanlou/下新建一个文件levenshtein_distance.py

参考代码如下:

注意:请务必自己独立思考解决问题之后再对照参考答案,一开始直接看参考答案收获不大。

def levenshtein_distance(first_word,second_word):
    if len(first_word)<len(second_word):
        return levenshtein_distance(second_word,first_world)
    if len(second_word)==0:
        return len(first_word)
    previous_row=range(len(second_word)+1)
    for i,c1 in enumerate(first_word):
        current_row=[i+1]
        for j,c2 in enumerate(second_word):
            # 计算增加,删除,修改次数
            insertions = previous_row[j + 1] + 1
            deletions = current_row[j] + 1
            substitutions = previous_row[j] + (c1 != c2)
            # 得到最小值,添加到current_row
            current_row.append(min(insertions, deletions, substitutions))
        # 储存previous_row
        previous_row = current_row
    #返回最后的编辑距离
    return previous_row[-1]

最长回文字串

所谓回文字串,即正着读和倒着读结果都一样的字符串,比如:a, aba, abccba 都是回文串,ab, abb, abca 都不是回文串。

暴力求解的思路:找到字符串的所有子串,遍历每一个子串以验证它们是否为回文串。一个子串由子串的起点和终点确定,因此对于一个长度为 n 的字符串,共有 n^2 个子串。这些子串的平均长度大约是 n/2,因此这个解法的时间复杂度是 O(n^3)。我们能不能提高效率呢,当然可以。这里给大家介绍马拉车算法。

求解回文串的问题,有很多巧妙的求解算法,这里仅介绍马拉车算法,其他求解算法无法一一介绍,感兴趣的同学请自行探索。

马拉车算法

马拉车算法(Manacher)由一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这是非常了不起的。让我们来看下马拉车算法的优越性在哪。

(1) 解决长度奇偶性带来的对称轴位置问题:Manacher 算法首先对字符串做一个预处理,在所有的空隙位置(包括首尾)插入同样的符号,要求这个符号是不会在原串中出现的。这样会使得所有的串都是奇数长度的。以插入#号为例:

aba->#a#b#a#
abba->#a#b#b#a#

插入的是同样的符号,且符号不存在于原串,因此子串的回文性不受影响,原来是回文的串,插完之后还是回文的,原来不是回文的,依然不会是回文的。

(2) 解决重复访问的问题:我们把一个回文串中最左或最右位置的字符与其对称轴的距离称为回文半径。Manacher 定义了一个回文半径数组 RL,用 RL[i]表示以第 i 个字符为对称轴的回文串的回文半径。我们一般对字符串从左往右处理,因此这里定义 RL[i]为第 i 个字符为对称轴的回文串的最右一个字符与字符 i 的距离。对于上面插入分隔符之后的两个串,可以得到 RL 数组。

char:    # a # b # a #
RL  :    1 2 1 4 1 2 1
RL-1:    0 1 0 3 0 1 0
i   :    0 1 2 3 4 5 6

char:    # a # b # b # a #
RL  :    1 2 1 2 5 2 1 2 1
RL-1:    0 1 0 1 4 1 0 1 0
i   :    0 1 2 3 4 5 6 7 8

上面我们还求了一下 RL[i]-1。通过观察可以发现,RL[i]-1 的值,正是在原本那个没有插入过分隔符的串中,以位置 i 为对称轴的最长回文串的长度。那么只要我们求出了 RL 数组,就能得到最长回文子串的长度。

于是问题变成了,怎样高效地求的 RL 数组。基本思路是利用回文串的对称性,扩展回文串。

我们再引入一个辅助变量 MaxRight,表示当前访问到的所有回文子串,所能触及的最右一个字符的位置。另外还要记录下 MaxRight 对应的回文串的对称轴所在的位置,记为 pos,它们的位置关系如下。

我们从左往右地访问字符串来求 RL,假设当前访问到的位置为 i,即要求 RL[i],在对应上图,i 必然是在 po 右边的(obviously)。但我们更关注的是,i 是在 MaxRight 的左边还是右边。我们分情况来讨论。

1) 当 i 在 MaxRight 的左边

我们知道,图中两个红色块之间(包括红色块)的串是回文的;并且以 i 为对称轴的回文串,是与红色块间的回文串有所重叠的。我们找到 i 关于 pos 的对称位置 j,这个 j 对应的 RL[j]我们是已经算过的。根据回文串的对称性,以 i 为对称轴的回文串和以 j 为对称轴的回文串,有一部分是相同的。这里又有两种细分的情况。

a.以 j 为对称轴的回文串比较短

这时我们知道 RL[i]至少不会小于 RL[j],并且已经知道了部分的以 i 为中心的回文串,于是可以令 RL[i]=RL[j]。但是以 i 为对称轴的回文串可能实际上更长,因此我们试着以 i 为对称轴,继续往左右两边扩展,直到左右两边字符不同,或者到达边界。

b.以 j 为对称轴的回文串比较长

这时,我们只能确定,两条蓝线之间的部分(即不超过 MaxRight 的部分)是回文的,于是从这个长度开始,尝试以 i 为中心向左右两边扩展,,直到左右两边字符不同,或者到达边界。

不论以上哪种情况,之后都要尝试更新 MaxRight 和 pos,因为有可能得到更大的 MaxRight。

具体操作如下:

  1. 令 RL[i]=min(RL[2*pos-i], MaxRight-i)
  2. 以 i 为中心扩展回文串,直到左右两边字符不同,或者到达边界
  3. 更新 MaxRight 和 pos
  1. 当 i 在 MaxRight 的右边

遇到这种情况,说明以 i 为对称轴的回文串还没有任何一个部分被访问过,于是只能从 i 的左右两边开始尝试扩展了,当左右两边字符不同,或者到达字符串边界时停止。然后更新 MaxRight 和 pos

参考代码如下;

def manacher(s):
    #预处理
    s='#'+'#'.join(s)+'#'

    RL=[0]*len(s)
    MaxRight=0
    pos=0
    MaxLen=0
    for i in range(len(s)):
        if i<MaxRight:
            RL[i]=min(RL[2*pos-i], MaxRight-i)
        else:
            RL[i]=1
        #尝试扩展,注意处理边界
        while i-RL[i]>=0 and i+RL[i]<len(s) and s[i-RL[i]]==s[i+RL[i]]:
            RL[i]+=1
        #更新MaxRight,pos
        if RL[i]+i-1>MaxRight:
            MaxRight=RL[i]+i-1
            pos=i
        #更新最长回文串的长度
        MaxLen=max(MaxLen, RL[i])
    return MaxLen-1

总结

本节内容介绍在 Python 里处理字符串的基本概念,以及正则表达式的使用,以及如何解决字符串匹配问题,编辑距离,回文串问题,我们这里介绍了 kmp 算法,利文斯顿算法,马拉车算法。其实字符串还有很多比较复杂的问题,例如最长无重复子串,最长连续序列,验证数字等问题,这里不再详细介绍,如果大家感兴趣,请自行探索。回顾下本节内容主要包含了以下内容:

  • 字符串的基本概念
  • 正则表达式
  • KMP 算法
  • 编辑距离
  • manacher 算法
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 字符串匹配KMP算法详解 1. 引言 以前看过很多次KMP算法,一直觉得很有用,但都没有搞明白,一方面是网上很少有...
    张晨辉Allen阅读 2,390评论 0 3
  • 本文首发于我的个人博客:尾尾部落 1. KMP 算法 谈到字符串问题,不得不提的就是 KMP 算法,它是用来解决字...
    繁著阅读 4,544评论 0 28
  • 上一篇KMP算法之后好几天都没有更新,今天介绍最长回文子串。 首先介绍一下什么叫回文串,就是正着读和倒着读的字符顺...
    zero_sr阅读 2,283评论 2 8
  •   应届生找工作的时候,第一轮笔试中,必然是有一个字符串的算法题。字符串处理是算法领域里非常重要的东西,有些是关于...
    知子不由阅读 1,362评论 0 0
  • 像是个幻境 层层茧丝之下 孕育生灵 水流声、风动声、梦也有声 今晚你没有来 阴郁的天 烟云里夜的脚步蹒跚 我撑起伞...
    折玫人阅读 227评论 9 14