zoukankan      html  css  js  c++  java
  • 数据结构:五 串

    1. 串的定义

    串(string)是由零个或多个字符组成的有限序列,又叫字符串

    一般记为 s = "a1a2······an"(n>=0)

    串中的字符数目 n 称为串的长度

    零个字符的串称为空串(null string)

    空格串:只包含空格的串,有内容有长度

    子串:串中任意个数的连续字符组成的子序列,其位置是子串的第一个字符在主串中的序号

     

    2. 串的比较

    给定两个串:s = "a1a2······an",t = "b1b2······bm",满足以下条件之一时 s < t

    • n < m,且 ai = bi(i = 1,2,··· ,n)
    • 存在某个 k<= min(m, n) ,使得 ai = bi( i= 1,2,··· ,k - 1),ak < bk

     

    3. 串的抽象数据类型

    ADT 串(string)
    Data 串中元素仅由一个字符组成,相邻元素具有前驱和后继关系
    Operation StrAssign(T,
    *chars):生成一个其值等于字符串常量chars的串T。 StrCopy(T,S):串S存在,由串S复制得串T。 ClearString(S):串S存在,将串清空。 StringEmpty(S):若串为空,则返回true,否则返回false。 StrLength(S):返回S的元素个数,即串S的长度。 StrCompare(S,T):若S>T,返回>0,S=T,返回=0,S<T,返回<0. Concat(T,S1,S2):用T返回由S1和S2联接而成的新串。 SubString(Sub,S,pos,len):串S存在,1<=pos<=Strlength(S), 且0<=len<=Strlength(S)-pos+1. 用Sub返回串S的第pos个字符起长 度为len的子串。 Index(S,T,pos):串S和T存在,T是非空串,1<=pos<=Strlength(S). 若主串S中存在和串T值相同的字串,则返回它在主 串S中第pos个字符之后第一次出现的位置,否则返回0 Replace(S,T,V):串S,T和V存在,T是非空串。用V替换主串S中出现 的所有与T相等的不重叠的子串。 StrInsert(S,pos,T):串S和T存在,1<=pos<=Strlength(S)+1.
                    在串S的第pos个字符之前插入串T。 SteDelete(S,pos,len):串S存在,
    1<=pos<=StrLength(s)-len+1.
                    从串S中删除第pos个字符起长度为len的子串。 endADT

     

    4. 串的存储结构

    串的顺序存储结构

    • 用一组地址连续的存储单元来存储串中的字符序列的
    •  

    串的链式存储结构

    • 一个结点可以存放一个字符,也可以考虑存放多个字符,最后一个结点若是未被占满时,可以用 “#” 或其它非串值字符补全

     

    5. 朴素的模式匹配算法

    子串的定位操作通常称作串的模式匹配

    对主串的每一个字符作为子串开头,与要匹配的字符串进行匹配。对主串做大循环,每个字符开头做 T 的长度的小循环,直到匹配成功或全部遍历完成为止

    最好情况

    • 一开始就匹配成功,如 "googlegood" 中找 "google"
    • 时间复杂度为:O(1)

    一般情况

    • 每次都是首字母就不匹配,如 "abcgoogle" 中找 "google"
    • 时间复杂度:O(n + m),n 为主串长度,m 为要匹配的子串长度

    最坏情况

    • 每次不匹配都发生在匹配串的最后一个字符上
    • 如 "0000000001" 中找 "0001"
    • 时间复杂度:(n - m + 1) * m

     

    6. KMP 模式匹配算法

    原理

    • 主串 S = "abcdefgabcdex",子串 T = "abcdex"
    • 前提:如果我们知道 T 串中首字符 "a" 与 T 中后面的字符均不相等
    • 而 T 串的第二位的 "b" 与 S 串中第二位的 "b" 已经判断是相等的,则 T 串中首字母 ”a“ 与 S 串中的第二位 "b" 是不需要判断也知道是不相等的

    next 数组

    • 我们把 T 串各个位置的 j 值的变化定义为一个数组 next,那么 next 的长度就是 T 串的长度

    next 数组值推导

    • 例一:T = "abcdex"

      • 1)当 j=1 时,next[1]=0;
      • 2)当 j=2 时,j 由 1 到 j-1 就只有字符 "a",属于其他情况 next[2]=1;
      • 3)当 j=3 时,j 由 1 到 j-1 串是 "ab",显然 "a" 和 "b" 不相等,属于其他情况,next[3] = 1;
      • 4)以后同理,所以最终此 T 串的 next[j] 为 011111
    • 例二:T = "abcabx"

      • 1)当 j=1 时,next[1]=0;
      • 2)当 j=2 时,同上例说明,next[2]=1;
      • 3)当 j=3 时,同上,next[3] = 1;
      • 4)当 j=4 时,同上,next[4] = 1;
      • 5)当 j=5 时,此时 j 由 1 到 j-1 的串是 "abca",前缀字符 "a" 与后缀字符 "a" 相等,因此可推算出 k 值为 2,因此 next[5]=2;
      • 6)当 j=6 时,j 由 1 到 j-1 的串是 "abcab",由于前缀字符 "ab" 与后缀字符 "ab" 相等,所以 next[3]=3;
      • 经验得出:如果前后缀一个字符相等,k 值是 2,两个字符 k 值是 3,n 个相等 k 值就是 n+1
    • 例三:T = "ababaaaba"

      • 1)当 j=1 时,next[1]=0;
      • 2)当 j=2 时,同上,next[2]=1;
      • 3)当 j=3 时,同上,next[3] = 1;
      • 4)当 j=4 时,j 由 1 到 j-1 的串是 "aba",由于前缀字符 "a" 与后缀字符 "ab" 相等,所以 next[4]=2;
      • 5)当 j=5 时,j 由 1 到 j-1 的串是 "abab",由于前缀字符 "ab" 与后缀字符 "ab" 相等,所以 next[5]=3;
      • 6)当 j=6 时,j 由 1 到 j-1 的串是 "ababa",由于前缀字符 "aba" 与后缀字符 "aba" 相等,所以 next[6]=4;
      • 7)当 j=7 时,j 由 1 到 j-1 的串是 "ababaa",由于前缀字符 "ab" 与后缀字符 "aa" 不相等,只有 "a" 相等,所以 next[7]=2;
      • 8)当 j=8 时,j 由 1 到 j-1 的串是 "ababaaa",只有 "a" 相等,所以 next[8]=2;
      • 9)当 j=9 时,j 由 1 到 j-1 的串是 "ababaaab",由于前缀字符 "ab" 与后缀字符 "ab" 相等,所以 next[9]=3;
    • 例四:T = "aaaaaaaab"

      • 1)当 j=1 时,next[1]=0;
      • 2)当 j=2 时,同上,next[2]=1;
      • 3)当 j=3 时,j 由 1 到 j-1 的串是 "aa",由于前缀字符 "a" 与后缀字符 "a" 相等,所以 next[3]=2;
      • 4)当 j=4 时,j 由 1 到 j-1 的串是 "aaa",由于前缀字符 "aa" 与后缀字符 "aa" 相等,所以 next[4]=3;
      • 5)......
      • 6)当 j=9 时,j 由 1 到 j-1 的串是 "aaaaaaaa",由于前缀字符 "aaaaaaaa" 与后缀字符 "aaaaaaa" 相等,所以 next[9]=8;

    KMP 模式匹配算法实现

    KMP 模式匹配算法实现改进

    • 缺陷

      • 主串 S = "aaaabcde",子串 T = "aaaaax",其 next 数组值分别为 012345
      • 当 i=5,j=5 时,"b" 与 "a" 不相等,因此 j=next[5]=4,此时 "b" 与 第 4 位置的 "a" 依旧不等
      • 依次往后,直到 j=next[1]=0时,根据算法,此时i++,j++,得到 i=6,j=1
      • 出现多余的判断

    • 优化办法

      • 由于 T 串的第二到五位置的字符都与首位的 "a" 相等,那么可以用首位 next[1] 的值去取代与它相等的字符后续 next[j] 的值
    • nextval 数组值推导

      • 例:T = "ababaaaba"

        • 先算出 next 数组的值分别为 001234223,然后再分别判断
        • 1)当 j=1 时,nextval[1]=0;
        • 2)当 j=2 时,因第二位字符 "b" 的 next 值是 1,而第一位就是 "a",它们不相等,所以 nextval[2] = next[2] = 1,维持原值;
        • 3)当 j=3 时,因第三位字符 "a" 的 next 值是 1,而第一位就是 "a",它们相等,所以 nextval[3] = nextval[1] = 0;
        • 4)当 j=4 时,因第四位字符 "b" 的 next 值是 2,所以与第二位的 "b",它们相等,所以 nextval[4] = nextval[2] = 1;
        • 5)当 j=5 时,next 值是 3,第五个字符 "a" 与第三个字符 "a" 相等,所以 nextval[5] = nextval[3] = 0;
        • 6)当 j=6 时,next 值是 4,第六个字符 "a" 与第四个字符 "b" 不相等,所以 nextval[6] = 4;
        • 7)当 j=7 时,next 值是 2,第七个字符 "a" 与第二个字符 "b" 不相等,所以 nextval[7] = 2;
        • 8)当 j=8 时,next 值是 2,第八个字符 "b" 与第二个字符 "b" 相等,所以 nextval[8] = nextval[2] = 1;
        • 9)当 j=9 时,next 值是 3,第九个字符 "a" 与第三个字符 "a" 相等,所以 nextval[9] = nextval[3] = 0;*
      • 总结改进过的 KMP 算法

        • 它是在计算出 next 值的同时,如果 a 位字符与它 next 值指向的 b 位字符相等,则该 a 位的 nextval 就指向 b 位的 nextval 值
        • 如果不等,则该 a 位的 nextval 值就是它自己 a 位的 next 的值
  • 相关阅读:
    包含min函数的栈
    量化交易系统的四个组成部分
    顺时针打印矩阵
    python正则表达式中re.M,re.S,re.I的作用
    二叉树的镜像
    树的子结构
    合并两个排序的链表
    反转链表
    命名元祖
    二叉树(二叉搜索树-AVL树-B树)
  • 原文地址:https://www.cnblogs.com/dc2019/p/13669362.html
Copyright © 2011-2022 走看看