数据结构-----读书笔记五(字符串的知识点)

本文介绍了字符串的基础概念,包括空串、子串等。重点讲解了KMP算法,阐述了其避免重复比较的核心思想,并详细解释了next数组的构建和作用。还列举了多种字符串操作的方法,探讨了字符串的顺序和链式存储方式。最后,讨论了KMP算法的时间复杂度和nextval数组的优化。
摘要由CSDN通过智能技术生成

串(string)是由零个或多个字符组成的有限序列,又叫字符串。串中字符的个数可以是0个也可以是多个,他们之间存在先后关系,是一个有限的序列。
空串 “”
空格串“ ”
子串“lie”, 主串”believe”

2018年6月2日的思考:
next数组以及优化后的nextval数组的推导的思路:
当位置i出现冲突的时候,我们可以从中得到前几位的情况,为了不必要的回溯,我们应该从那个位置开始匹配,这个就我们我们需要思考的核心问题。(减少不必要的比较是关键的问题,而解决这个核心的问题的主要处理的目标在于模式串,而不在与匹配的主串的问题,我们抽取出了模式串中隐含的信息,然后把这个信息记录下来,记录下来的信息就是二个数组,指导我们,如果在这个位置发生冲突的时候,我们应该移动哪个地方来重新开始,可以说是优化版的从哪跌倒从哪爬起问题。)

对于next数组而言,最主要的基础在于寻找模式串中重复字串的问题。用几句话来介绍,在第一个位置,固定是0, 也就是说在第一个位置的字符发生冲突的时候,我们移动一位来进行比较;在其他位置,我们考虑从此位置之前的子串的重复的模式,重复模式最长的就是这个数值,也就是从第几位开始进行比较的问题,在这个问题中,最关键的是怎么找重复模式,限制条件是,二个找到的串,一个串的开头必须是第一个位置的字符,而另一个串的结尾必须是字串的最后一个字符;在其余的情况,设为1就好了,也就是代表是从第一个字符开始匹配的意思。

代码实现求解next数组得关键点:前缀是不变的,后缀是改变的。可以通过编程技巧来实现最优的寻找模式串子串中的重复模式的目的。
此处最重要的一个想法在于前缀是相同的,后缀却各有各的不同,所以,往后扩展后缀,前缀往前回溯,可以得到解决。

对nextval数组而言,是因为next数组特有的问题,尤其在一些重复出现的情况中,如果
出现错误的位置和回溯到的那个位置的数值相同,则继续往前回溯,如果不相同,则回溯到那个位置即可以结束。

普遍来说串中的一些操作(C)
1,生成一个值等于字符串常量的字符串StrAssign(T, *chars)
2, 复制一个字符串,得到字符串StrCopy(T, S)
3,清空字符串ClearString(S)
4,判断字符串是否为空StringEmpty(S)
5, 得到字符串的长度StrLength(S)
6,比较二个字符串的大小StrCompare(S,T)
7,连接二个字符串形成新串Concat(T, S1, S2)
8,返回某位开始的长度为某长度的一个子串SubString(Sub, S, pos, len)
9,得到从某个位置开始的子串出现的第一个位置Index(S,T,pos)
10,由一个子串代替主串中的另一个子串Replace(S,T,V)
11,在某位置插入某子串StrInsert(S, pos, T)
12,删除某位置之后的某长度的子串StrDelete(S, pos, len)

涉及到的串的一些操作:(python)
参考博客:https://www.cnblogs.com/songqingbo/p/5126957.html
1,字符串首字母大写capitalize(S)
2, 将大写字母转换为小写字母casefold()
3, 填充字符来扩充字符串到指定长度center(15,’*’)
4, 统计某个字符在字符串中出现的次数,或在字符串指定区间上完成上述操作count(‘a’,0,5)
5, 对字符串进行编码encode()
6, 判断字符串是否以某个字符结尾endswith()
7,将制表符转换为指定宽度的tab键分割expandtabs(),默认为tabsize = 8
8, 在字符串中查找指定的字符串find()
9, 格式化输出字符串format()
10,判断某子串是否在主串中contains()
11, 在主串中查找指定的子串index()
12, 字符串的连接join()
13, 判断字符串中是否包含数字,字母isalnum()
14, 判断字符串总是不是只由字母组成isalpha()
15,判断字符串中是否只包含十进制字符isdecimal()
16, 判断字符串中是否只由数字组成isdigit()
17, 判断字符串是否以字母作为开头isidentifier()
18, 判断字符串中是否只由数字组成,针对unicode对象isnumeric()
19, 判断字符串中所有字符是否都属于可见字符isprintable()
20, 判断字符串中是否均为空格isspace()
21, 判断字符串是否适合作为标题,即每个单词的首字母均需要大写istitle()
22, 判断字符串中所有字符是否都是大写字母isupper()
23, 返回一个原字符串左对齐,并填充到指定长度ljust()
24, 将所有字母均变成小写字母lower()
25, 去除字符串左边开头的空格lstrip()
26, 去除字符串右边开头的空格rstrip()
27, 去除字符串二边的空格strip()
28, 用于创建字符映射的转换表maketrans()
29, 根据指定的分隔字符将字符串来进行分割partition()
30, 将某个字符串代替为另一个字符串,最多代替的次数可以有限制replace()
31, 利用空格(默认)将字符串进行分隔split()
32, 在字符串后添加某字符或字符串_add_()
33, 判定指定的字符串是否包含在字符串中_contains_()
34, 判定字符串是否相等_eq_()

串的实现:
1,顺序存储的方式
因为需要预估串的最大值的情况,并且在进行串的操作,如替换,连接二串的时候,可能会产生溢出的错误。
2,链式存储的方式
在链式存储中,需要考虑的一个问题在于,一个结点到底要存储多少个字符,因为,一个结点若只存储一个字符的话,效率又太低了,但存储多个的话,在串的某些操作上又会有多困难。
总体上来说,串的顺序存储方式相比于链式存储方式而言更加的方便和灵活。

串最重要的操作:模式匹配(即子串的定位操作)
1, 朴素的模式匹配算法
利用主串中的每个字符来作为子串的开头来进行匹配,对主串来进行大循环,然后对每个子串来进行小循环。
显然,有点太简单,太低效了
2, KMP模式匹配算法
避免重复遍历的情况,D.E.Knuth, J.H.Morris和V.R.Pratt发表了一个模式匹配的算法,称为可努特-莫里斯-普拉特算法,简称KMP算法。
KMP算法中,是为了避免重复的比较而产生的一种算法。主要的思想是保持主串的比较的位置的不减小,改变的是子串的大小的变化问题。尽量排除多余的冗余的比较,已比较过得就不再进行比较。我们对其进行通俗的理解。
(通俗的理解)
对于KMP模式匹配算法,主串是我们想要做匹配的字符串,子串是我们匹配的模式,在匹配模式中,我们期望记录已经比较过的那部分,然后不对已经比较过的部分进行比较,减少重复比较,来提高模式匹配的效率。
我们为了记录已经比较过的部分,为了挖掘模式串中的规律,我们建立了next数组,在next数组中,我们存储在当前这个位置之前已经有多少个子字符串已经比较过了,然后直接在这个地方对二个部分来进行比较,也就是说在主串中,我们的那个下标值是不进行修改的,修改的是模式串中的下标值,为了减少模式串在主串中的比较。
这里写图片描述
构建了next数组。(只要与自己的规则一致即可,不要太在意初始的位置为0还是初始的位置为-1,这些都是可调节,可变化的量。)
在这个时候,我有点像偷懒,不太想要描述next数组的定义,等会我抠图上来,直接解释一下next的定义的,在j = 1时,next数组为1;然后j在j-1个字符中,前多少与后多少位是重复的状态,但前多少与后多少有一个限制,至多是前j-2位;在其他情况下,next数组值为1。

next数组是为了记录在模式串中有多少与开头重复的模式,如果为重复的模式即不需要来重新比较,这样可以减少多余的比较。。。

KMP算法的next数组的程序:(C)

void get_next(String T, int * next)
{
   int i, j;
   i = 1;
   j = 0;
   next[1] = 0;
   while (i < T[0])
   {
      if (j == 0 || T[i] == T[j])
      {
         ++i;
         ++j;
         next[i] = j;
      }
      else
         j = next[j];
   }
}

在上述的代码中,精髓在于怎么巧妙的实现对于最大重复子串判断的那个部分。
实现KMP模式匹配算法(C)

int Index_KMP(String S, String T, int pos)
{
   int i = pos;
   int j = 1;
   int next[255];
   get_next(T, next);
   while(i <= S[0] && j <= T[0])
   {
      if (j == 0 || S[i] == S[j])
      {
         ++i;
         ++j;
      }
      else
      {
         j = next[j];
      }
   }
   if (j > T[0])
       return i - T[0];
   else
       return 0;
}

朴素的模式匹配算法的时间复杂度为O((n-m+1)*m);对于KMP模式匹配算法的时间复杂度为O(n+m);只有在主串和子串存在很多的“部分匹配”的时候,KMP算法才能体现他的优势所在。
对于KMP算法而言,有其缺点,我们通过改进next数组来改进KMP算法。缺点在于模式串中有很多的重复项,这样需要重复比较多次,所以,提出了利用nextval数组来代替next,进行改良。
nextval的实现(c语言版本)

void get_nextval(String T, int *nextval)
{
   int i, j;
   i = 1;
   j = 0;
   nextval[1] = 0;
   while(i < T[0])
   {
       if (j == 0 || T[i] == T[j])
       {
          ++i;
          ++j;
          if (T[i] != T[j])
          {
             nextval[i] = j;
          }
          else
             nextval[i] = nextval[j];
       }
       else
           j = nextval[j];
   }
}

在我看来,next矩阵类似于动态规划的存在,这种类型的问题值得我们去借鉴里面的思想,利用前面的计算,避免冗余的计算的思想。。
python的实现

# -*- coding: utf-8 -*-

class String_l():

    def __init__(self):
        self._list = []
        self._length = 0

    def is_empty(self):
        if self._length == 0:
            return 1
        else:
            return 0

    def get_length(self):
        return self._length

    def clear_string(self):
        self._length = 0
        self._list = []

    def insert_string(self, string1):
        for s in string1:
            self._list.append(s)
            self._length += 1

def get_next(string1):
    next = []
    next.append(int(string1[0]))
    i = 1
    j = 0
    next.append(0)
    while i < next[0]:
        if string1[i] == string1[j] or j == 0:
            i += 1
            j += 1
            next.append(j)
        else:
            j = next[j]
    return next

def get_nextval(string2):
    nextval = [0] * len(string2)
    nextval[0] = int(string2[0])
    i = 1
    j = 0
    while i < nextval[0]:
        if string2[i] == string2[j] or j == 0:
            i += 1
            j += 1
            if string2[i] != string2[j]:
                nextval[i] = j
            else:
                nextval[i] = nextval[j]
        else:
            j = nextval[j]
    return nextval

next = get_next('5ababc')
print next
nextval = get_nextval('8ababaaab')
print nextval


努力吧,少女,,前面还有很多大山值得跨越~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值