• 数据结构-串的定长顺序存储


    串的定长顺序存储类似于线性表的顺序存储结构,用一组连续的存储单元存储串值的字符序列。

    在串的定长顺序存储结构中,按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区,则可以用定长数组表示:

    1 /*串定长顺序存储表示*/
    2 #define MAXSTRLEN 255        //串在MAXSTRLEN大小
    3 typedef unsigned char SString[MAXSTRLEN + 1];    //所有串的0号单元存储串的长度

    串的基本操做函数如下:

      1 /*生成一个其值等于chars的串T*/
      2 Status StrAssign(SString &T, char chars[])
      3 {
      4     if(strlen(chars) > MAXSTRLEN)
      5         return ERROR;
      6     T[0] = strlen(chars);
      7     for (int i = 0; i <= T[0]; i++)
      8         T[i+1] = chars[i];
      9     return OK;
     10 }
     11 
     12 /*由串S赋值的到串T*/
     13 Status StrCopy(SString &T, SString S)
     14 {
     15     int i = 1;
     16     for (; i <= S[0]; i++)
     17         T[i] = S[i];
     18     T[0] = S[0];
     19     return OK;
     20 }
     21 
     22 /*判断串S是否为空,若S为空串侧返回true,否则返回false*/
     23 bool StrEmpty(SString s)
     24 {
     25     if(s[0] == 0)
     26         return true;
     27     else
     28         return false;
     29 }
     30 
     31 /*若S>T,则返回值大于0;若S=T,则返回值等于0;若S<T则返回值小于0*/
     32 Status StrCompare(SString S, SString T)
     33 {
     34     for (int i = 1; i <= S[0] && i <= T[0]; ++i)
     35         if(S[i] != T[i])
     36             return S[i] - T[i];
     37     return S[0] - T[0];
     38 }
     39 
     40 /*返回串的长度*/
     41 int StrLength(SString S)
     42 {
     43     return S[0];    //串的第0个存储放的串的长度
     44 }
     45 
     46 /*重置串为空串*/
     47 Status ClearString(SString &S)
     48 {
     49     S[0] = 0;    //因为是顺序存储,只要将长度标志设为0即是代表已经清空串
     50     return OK;
     51 }
     52 
     53 /*用T返回由串S1和串S2连接成的新串。若未截断,则返回true,否则返回false*/
     54 bool Concat(SString &T, SString S1, SString S2)
     55 {
     56     bool uncut = true;
     57     if(S1[0] + S2[0] <= MAXSTRLEN)    //未截断
     58     {
     59         for (int i = 1; i <= S1[0]; i++)
     60             T[i] = S1[i];
     61         for (int i = 1; i <= S2[0]; i++)
     62             T[i + S1[0]] = S2[i];
     63         T[0] = S1[0] + S2[0];
     64         uncut = true;
     65     }
     66     else if(S1[0] < MAXSTRLEN)    //截断;只取S2的一部分
     67     {
     68         int i = 1;
     69         for ( ; i <= S1[0]; i++)
     70             T[i] = S1[i];
     71         for (i = 1; i <= MAXSTRLEN-S1[0]; i++)
     72             T[i + S1[0]] = S2[i];
     73         T[0] = MAXSTRLEN;
     74         uncut = false;
     75     }
     76     else                    //截断;仅取S1
     77     {
     78         for (int i = 0; i <= MAXSTRLEN; i++)
     79             T[i] = S1[i];
     80         uncut = false;
     81     }
     82     return uncut;
     83 }
     84 
     85 /*用sub返回串S的第pos个字符起的长度为len的子串*/
     86 Status SubString(SString &Sub, SString S, int pos, int len)
     87 {
     88     if(pos < 1 || pos > S[0] || len < 0 || len > S[0]-pos+1)
     89         return ERROR;
     90     for (int i = 1; i <= len; i++)
     91         Sub[i] = S[pos+i-1];
     92     Sub[0] = len;
     93     return OK;
     94 }
     95 /*用串V替换主串中出现的所有*/
     96 Status Replace(SString &S, SString T, SString V)
     97 {
     98     if(StrEmpty(T))
     99         return ERROR;
    100     int i = 1, j = 1;
    101     int m = StrLength(T), n = StrLength(V);
    102     while(i <= S[0])
    103     {
    104         j = Index(S,T,i);
    105         StrDelete(S,j,m);
    106         StrInsert(S,j,V);
    107         i+=n+1;
    108     }
    109     return OK;
    110 }
    111 
    112 /*在串S的pos位置之前插入串T*/
    113 Status StrInsert(SString &S, int pos, SString T)
    114 {
    115     if(pos < 1 || pos > S[0]+1)
    116         return ERROR;
    117     if(S[0]+T[0] <= MAXSTRLEN)    //完全插入
    118     {
    119         for (int i = S[0]; i >= pos; i--)
    120             S[i+T[0]] = S[i];
    121         for (int i = pos; i < pos+T[0]; i++)
    122             S[i] = T[i-pos+1];
    123         S[0] = S[0]+T[0];
    124         return OK;
    125     }
    126     else    //不完全插入
    127     {
    128         for (int i = MAXSTRLEN; i >= pos; i--)
    129             S[i] = S[i-T[0]];
    130         for (int i = pos; i < pos+T[0]; i++)
    131             S[i] = T[i-pos+1];
    132         S[0] = MAXSTRLEN;
    133         return ERROR;
    134     }
    135 }
    136 
    137 /*从串S中删除第pos个字符起的长度为len的子串*/
    138 Status StrDelete(SString &S, int pos, int len)
    139 {
    140     if(pos < 1 || pos > S[0]-len+1 || len < 0)
    141         return ERROR;
    142     for (int i = pos+len; i <= S[0]; i++)
    143         S[i-len] = S[i];
    144     S[0] -= len;
    145     return OK;
    146 }
    147 
    148 /*销毁串S*/
    149 Status DestroyString(SString &S)
    150 {
    151     free(S);
    152     return OK;
    153 }
    154 
    155 /*输出串*/
    156 void StrPrint(SString T)
    157 {
    158     for (int i = 1; i <= T[0]; i++)
    159         printf("%c",T[i]);
    160     printf("
    ");
    161 }

    还有一个函数Index函数。做字符串匹配用,这里拿出来单独讨论

     1 /*若主串S中存在和串T值相同的子串;
     2 /*则返回他在主串S中第pos个字符之后第一次出现的位置;
     3 /*否则函数值为0*/
     4 int Index(SString S, SString T, int pos)
     5 {
     6     /*调用串基本操作的方法,也是Index函数的思想步骤*/
     7     //if(pos > 0)
     8     //{
     9     //    int n = StrLength(S);    //获取串的长度
    10     //    int m = StrLength(T);
    11     //    int i = pos;
    12     //    SString sub;
    13     //    while(i <= n-m+1)        //循环值从pos到串S的最后一个T长度位置
    14     //    {
    15     //        SubString(sub,S,i,m);    //获取子串,S的第i个位置开始长度m-1的子串
    16     //        if(StrCompare(sub,T) != 0)    //判断获取的子串与串T是否不等;如果相等返回i的值也就是第一次出现的位置
    17     //            ++i;
    18     //        else
    19     //            return i;
    20     //    }
    21     //}
    22     //return 0;    //没有出现返回0
    23 
    24     /*定位函数Index的模式匹配算法
    25     /*算法的基本思想:
    26     /*从主串S的第pos个字符起和模式的第一个字符比较之,
    27     /*若相等,则继续逐个比较后续字符;
    28     /*否则从主串的下一个字符起再重新和模式的字符比较之。
    29     /*以此类推,直至模式T中的每个字符依次和主串S中的一个连续的字符序列相等,则匹配成功;
    30     /*函数值为和模式T中的第一个字符相等的字符在主串S中的序号。
    31     /*否则匹配不成功,函数值为零。*/
    32     int i = pos,j = 1;
    33     while(i <= S[0] && j <= T[0])
    34     {
    35         if(S[i] == T[j])    //继续比较后续字符
    36         {
    37             i++;
    38             j++;
    39         }
    40         else    //指针后退重新开始匹配
    41         {
    42             i = i-j+2;
    43             j = 1;
    44         }
    45     }
    46     if(j > T[0])
    47         return i - T[0];    //匹配成功
    48     return 0;                //匹配失败
    49 }

    其中这个函数内写了两种方法:第一种调用基本函数的方法,第二种模式匹配算法。

    但模式匹配算法还有一个很是经典的算法模式匹配的改进算法-KMP算法;

    其改进在于:每当一趟匹配过程中出现字符比较不等时,不需回溯i指针,而是利用已经得到的“部分匹配”的结果将模式向右滑动尽可能远的一段距离后,继续进行比较。

    KMP算法的基本思想就是这样,剩下的百度搜索全是我就不再细说,还是按我的习惯直接上代码。

     1 void get_next(SString T, int *next) {
     2   int i=1;
     3   next[1]=0;
     4   int j=0;
     5   while (i<T[0]) {
     6     if(j==0 || T[i]== T[j]) {
     7       ++i;  ++j;  next[i] = j;
     8     } else j= next[j];
     9   }
    10 }
    11 
    12 int Index_KMP(SString S, SString T, int pos) {
    13   // 利用模式串T的next函数求T在主串S中第pos个字符之后的位置的
    14   // KMP算法。其中,T非空,1≤pos≤StrLength(S)。
    15   int next[255];
    16   int i = pos;
    17   int j = 1;
    18   get_next(T, next);
    19   while (i <= S[0] && j <= T[0]) {
    20     if (j == 0 || S[i] == T[j]) {  // 继续比较后继字符
    21       ++i;  ++j;
    22     } else j = next[j]; // 模式串向右移动
    23   }
    24   if (j > T[0]) return  i-T[0];   // 匹配成功
    25   else return 0;
    26 }

    KMP算法时间复杂度O(m)。通常,模式串的长度m比主串的长度n要小的多,因此对整个匹配算法来说,所增加的这点时间是值得的。

    但是,虽然模式匹配算法时间复杂度是O(n*m),但是一般情况下,其实际的实行时间近似于O(n+m),因此至今仍被采用。

    KMP算法仅当模式与主串之间存在许多“部分匹配”的情况下才显得比模式匹配算法快得多。但是KMP算法的最大特点是指主串的指针不需回溯,整个匹配过程中,对主串仅需从头至尾扫描一遍,这对处理从外设输入的庞大文件很有效,可以边读入边匹配,而无需回溯重读。

    但对于next函数前面所写的在某些情况下有一个缺陷,例如在模式aaaab,主串aaabaaaab匹配时,当i=4,j=4时ch[4]!=t.ch[4]。由next[j]的指示还需要进行i=4,j=3;i=4,j=2;i=4,j=1这三次比较。实际上,因为模式中第1,2,3个字符和第4个字符都相等,因此不必要在和主串中第4个字符相比较,而可以将模式一下子滑动4个位置。直接进行i=5,j=1时的字符比较。

    j 1 2 3 4 5
        模式 a a a a b
       next[j] 0 1 2 3 4
    nextval[j] 0 0 0 0 4

    改进的next函数如下:此时匹配算法不变;

    void get_nextval(SString T, int *nextval) {
      // 求模式串T的next函数修正值并存入数组nextval。
      int i = 1;
      int 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];
      }
    }
  • 相关阅读:
    数位DP入门
    划分树
    CodeForces #362 div2 B. Barnicle
    CodeForces #363 div2 Vacations DP
    CodeForces #368 div2 D Persistent Bookcase DFS
    解决Ubuntu 下 vi编辑器不能使用方向键和退格键问题
    python之爬虫爬有道词典
    hdu 5145 NPY and girls 莫队
    hdu 6185 Covering 矩阵快速幂
    字典树求异或值
  • 原文地址:https://www.cnblogs.com/ABook/p/5638303.html
Copyright © 2020-2023  润新知