[关闭]
@ljt12138 2017-02-11T14:33:22.000000Z 字数 4547 阅读 1502

字符串的组合性质学习笔记

算法

摘要:本文主要给出字符串的定义和若干简单组合性质的证明,并简要分析字符串相关算法。

1. 基本定义

定义1.1: 由有限字母表 中元素构成的数组P[1..m]称为一个字符串,m称为字符串的长度,记作 。记 是该字母表下所有有限字符串构成的的集合。如果 ,记

定义1.2: 对于两个字符串A, S,如果 ,称A为S的前缀,记作 ,或 。对于两个字符串B, S,如果 ,称B为S的后缀,记作 ,或

引理1.1: 都是等价关系。

证明:显然

引理1.2(前缀、后缀重叠引理): 对于字符串S的两个前缀,对于两个后缀

证明:显然

定义1.3:两个字符串的连接。记

引理1.3: 同态于非负整数加群。

证明:显然

2. Border与周期

定义2.1: 对于一个字符串S,如果 ,称为S的一个border。S所有border中长度最长且不为其本身的称为其最长非平凡border,记作

引理2.1(border的传递性): A是B的border,B是C的border,则A是C的border。

证明:显然

引理2.2: 是S的第二长非平凡border。由此可以知道, 是S的第n长非平凡border。

证明:第一部分用反证法,设 ,如果存在border满足 ,由于引理1.2,。根据定义,, 由于A为S的border,有 。由于,因而,所以。由于 ,可知 是A的border,与 矛盾。第二部分用归纳法,这里省略。

定义2.2: 如果S的一个前缀,满足,称i是S的周期。

引理2.3: i是S非平凡的周期 是S的非平凡border。

证明:
1. 考虑与S相交的形态,设k为满足的最小正整数,则 ,设 则显然。由于, 因而T为S的border;
2. border长度长于字符串一半时:

  1. S_i: |--s4--|
  2. S_i: |--s2--|
  3. Border: |---------|
  4. S: |----------------|
  5. Border:|---------|
  6. S_i: |--s1--|
  7. S_i: |--s3--|
  1. Border: |-s2-|
  2. S: |----------------|
  3. Border:|-s1-|
  4. P: |-S1-|------|-s2-|------|
  5. P: |-----------|-----------|

定理2.4(弱周期定理): p和q是S的周期,满足 为S的周期。

证明:令,根据更相减损术的证明我们知道,只需要证明q-p为S的周期即可。由引理2.2可以知道 是S的border。设,由于,故两个周期不能都大于字符串一半长,两个border不能都小于字符串一半长。由于的border,那么;同理的border,那么。由于,则。根据定义可以知道是S的border,根据引理2.3,d是S的周期。

弱周期定理是一个强大的定理,在很多证明中我们都要用到,请读者认真理解。

推论2.5: 不小于字符串一半长的border长度成等差数列;小于字符串一半长的周期长度成等差数列,且公差等于最小的周期长。

证明:两命题等价,我们只证明第二个。取最小的周期长,则对于任意一个周期满足,根据弱周期定理,也是周期,因为i是最小周期,,则。接着我们只需要证明,都有k是周期,这是显然的,因为只需要将重复次便可以得到,其显然为周期。

定理2.6: 一个字符串所有的border可以被划分成段等差数列;一个字符串所有的周期可以被划分成段等差数列。

证明:考虑将字符串|S|所有border按长度划分为。根据推论2.5,最后一段中是正确的。对于,考虑长度在的border长为,则的长度大于一半的border,成等差数列,第一个命题得证。根据引理2.3不难证明第二个命题。

算法2.1:快速计算前缀的

我们定义,下面的算法可以计算出所有前缀的最长非平凡border。

  1. void calc_pi(int pi[], char str[], int len)
  2. {
  3. pi[1] = 0;
  4. int j = 0;
  5. for (int i = 2; i <= len; i++) {
  6. while (j && str[j+1] != str[i]) i = pi[i];
  7. if (str[j+1] == str[i]) ++j;
  8. pi[i] = j;
  9. }
  10. }

正确性:核心在于算法第6行,即如果的border且不是S的border,那么都不是S的border。我们用反证法,假设是S的border,则的border,也是的border,和的定义矛盾。

时间复杂度:由于j的增加只出现在第7行,而6行的while循环则会使j减小,由于增加最多为|S|,因而while循环运行次数为,总复杂度为

定义2.3: 对于两个字符串P, S,如果S存在一个后缀满足,称P在的位置匹配了S。

定理2.4: 如果,则P对S所有匹配的位置成一等差数列。

证明:我们取最小的匹配位置i和第二小匹配位置j,对于之后任意一个匹配位置k,由于任意两个匹配都相交。

  1. S: |--------------------|
  2. P1: i----P1-----|
  3. P2: j----P2-----|
  4. P3: k----P3-----|

由于P1是的border,因而j-i是的周期;由于P1是的border,k-i是的周期,因而也是的周期。根据弱周期引理, 也是的周期。如果j-i不是的最小周期,设最小周期为u,则有最长border Px,

  1. S: |--------------------|
  2. P1: i----P1-----|
  3. Px: ------Px-----|
  4. Px2: |-----Px2---|
  5. Px: |------Px-----
  6. P2: j----P2-----|
  7. P3: k----P3-----|

观察到Px相交的区域包含一个也构成一个匹配,这与P2为第二小的匹配位置矛盾。因而j-i是的最小周期。则

下面我们只需要说明位置可以匹配:

  1. S: |--------------------|
  2. P1: i----P1-----|
  3. P2: j----P2-----|
  4. P3: p----P3-----|
  5. P4: k----P4-----|
  6. ...

我们在所有满足的位置构造Px,直到最后一个匹配。以P4为最后一个为例,我们需要证明P3是一个匹配。由于P2是的border,k-j也是其周期,则k-j也是的周期,因而P2是的border,P3因而也是一个匹配。用类似的构造法可以证明原命题。

算法2.2:KMP字符串匹配算法

  1. void kmp(char S[], int Sl, char P[], int Pl, int pi_P[])
  2. { // pi_P[]可以由算法2.1算出
  3. int j = 0;
  4. for (int i = 1; i <= S1; i++) {
  5. while (j && P[j+1] != S[i]) j = pi_P[j];
  6. if (P[j+1] == S[i]) ++j;
  7. if (j == Pl) {
  8. printf("Matched Successfully on %d\n", i-Pl+1);
  9. j = pi_P[j]; // find again
  10. }
  11. }
  12. }

算法正确性和复杂度的证明类似于算法2.1,请读者类比2.1证明之。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注