- 一、
串: 串就是字符串,本质上就是线性结构,其中可以存放各种字符,是线性表的一个具体表现形式。
- 二、
串表示法: 串的表示方法主要有顺序存储,包括预定长度的串(定长顺序表示)和可变长度的串(堆分配存储);还有链表表示方法,包括存储结点大小为1的表示,还有每一个链表结点存储k个字符的链表表示。
链表表示法中,如果每一个结点存放一个字符的话,存储密度比较低,但是运算操作效率比较高;反之,每个节点都存储k个字符的话,操作起来会费劲一些,但是存储密度足够好。
- 三、
串的操作: - 对串的操作和对链表的操作差不多,唯一的区别就是如果每个节点中存放了K个字符的话,就需要考虑不被k整除的串索引的情况了。
主流语言中都有内置的串操作函数,这里也没必要再实现一遍串的数据结构了。但是串操作有一个和线性表很不同的就是串的模式匹配,更复杂的情况也就是正则表达式。
正常的串模式匹配算法就是在主串中以此匹配模式串,如果匹配成功,则返回首个字符的串索引,如果匹配失败,就继续匹配下一个字符。但是这种最基本的方法效率并不高,时间复杂度 o(m* n),两个参数分别代表字符串的长度和模式串的长度,空间复杂度o(1),这里没有考虑存放匹配的索引列表。
KMP算法是在此基础上的一种改进,它利用m维数组或其他线性结构存放了下一次匹配的模式位置,所以利用空间换取了一部分时间优化,但是具体时间复杂度会根据具体的字符串有较大的差别,但是性能如果最恶劣的话,也是o(m*n)。但是在很多时候,普通算法的执行时间并不比KMP慢多少,而且空间复杂度很低,所以现在依然被采用。
这里先略去正则表达式里复杂的语法规则不管,用Python实现一个普通搜索算法,然后再写一个最简单的KMP算法。
def pattern_match(str, pattern): # 普通模式匹配算法,返回所有的索引
index = []
for _ in xrange(0, len(str)):
i = _
j = 0
while j < len(pattern):
if i < len(str) and str[i] == pattern[j]:
i += 1
j += 1
else:
break
if j == len(pattern):
index.append(_)
return index
str = '90hnfuewdiohnfge'
pattern = 'hnf'
index = pattern_match(str, pattern)
print index
下面是 KMP算法,我自己写的:
def kmp_pattern_match(str, pattern):
index = []
j = 0
for i in xrange(0, len(str)):
j = get_next(pattern, j)
while j < len(pattern):
if i < len(str) and str[i] == pattern[j]:
i += 1
j += 1
else:
break
if j == len(pattern):
index.append(i-j)
j = 0
return index
def get_next(pattern, index): # index 范围为 1 到 len(pattern),算上 len(pattern)
next_list = next_index(pattern)
return next_list[index]
def next_index(pattern): # 返回一个列表,表示对应索引的下一跳索引
next_list = [0, 0]
for _ in xrange(2, len(pattern)):
temp_list = [0]
for i in xrange(1, _):
if pattern[0:i] == pattern[_-i:_]:
temp_list.append(i)
next_list.append(max(temp_list))
return next_list
在KMP上进一步扩展就是RE,正则表达式。但是记忆正则匹配是个麻烦的事情,前一段时间使用正则表达式处理文本比较多,所以很多字符的含义都记得,但是现在过了一段时间没有使用,很多的正则语法又忘记了。如果要自己实现一个正则表达式的话,我认为需要解决的几个问题是:栈队列存放模式,把模式和函数组合起来,模式匹配算法。