zoukankan      html  css  js  c++  java
  • 数据结构与算法系列研究三——字符串

    字符串的研究和KMP算法分析和实现

    一、串的定义

      串是计算机非数值处理的基本对象。串是一种特殊的线性表,它的每个结点仅由一个字符组成,并且单个元素是无意义的。
       1、串(string):是由0个或多个字符组成的有限序列,记作:
              S=“a1a2...an”  (n>=0)
              其中:S是串名,两个双引号括起来的字符序列为串的值。双引号不属于串。
                       ai(1<=i<=n)为字母、数字或其它符号。
                      空格符是一个有效字符。
       2、串中所有字符的个数n为串的长度。长度为0的串称为空串。
       3、空格串:全部由空格符组成的字符序列。
       4、子串:串中任意连续个字符的序列称为该串的子串。
       5、主串:包含该子串的串称为主串。
       6、字符在串中的序号称为该字符在串中的位置
       7、子串在串中的位置:用子串第一个字符在主串中的位置来表示。空串是任意串的子串。任意串是自身的子串。
       8、串常量:不能改变其值的量为常量,串常量一般用直接量表示。
           串变量:用于存放字符串值,并且其值可以改变的量。

       相关运算:(黄色为基本子集,有这几种操作可以产生其他复杂操作)
         Strassign (&S,chars):将常数串赋值给S,对应于strcopy。
         Strlength(S):求串S的长度,对应于strlen(s)。
         Strcompare(S1,S2):比较串S1和S2,对应于strcmp(s1,s2)。
         Concat(&S,S1,S2):将串S1和S2联接成一个串,并赋给S。
         Substring(&Sub,S,pos,len):在S串中求pos开始的长度为len的子串。
         Clearstring(&S):将串S置成空串。
         Strcopy (&S,S1):将串S1复制到变量S
         Strempty(&S):判断串S是否为空串。
         Index(S,Sub,pos):求子串sub在串S的pos开始后出现的位置。
         Replace(&S,Sub,T);用Sub替换S中所有与T相等的不重叠的子串。
         Strinsert(&S,pos,Sub):在串S的pos位置前插入子串Sub。
         Strdelete(&S,pos,len):在串S中删除pos位置开始长度为len的子串。
         Destroystring(&S):撤消串S。
      串的表示与实现—堆分配存储:

      分配一组地址连续的与串长一致的存储单元存放串值字符序列。与定长顺序表示的区别:采用动态分配;串空间与串长一致,串长变化将引起串空间的重新分配。
      堆存储表示:


           typedef struct{
             char *ch;
             int length;
           }HString

    串的表示与实现—块链表示
      块:一组连续的字符。
      块链存储表示:把串分成指定等长的块,每一块用一个结点表示,把各块链成一个链表。
                         当一个结点不满时,用特殊字符(如‘#’)填充。
                         若块的长度为1,就是以单字符为结点的链表结构。
                         块的大小与存储密度有关:存储密度=串值所占存储位/实际分配存储位。

      单字符结点:插入、删除方便;存储密度小,存储占用量大。

     

      多字符结点:存储密度大;插入、删除存在结点的分离,降低存储密度。

     

    二、字符串的KMP算法

      2.1、串的模式匹配算法
         串的模式匹配:求子串P在主串T中的位置的定位操作称为串的模式匹配。
         模式串:子串。
      2.2、简单的模式匹配算法:
         从主串T的第一个字符起,与模式串P的第一个字符比较,若相等,则继续逐个比较后继字符,否则从主串T的第二个字符起再重新和模式串P的第一个字符比较。依次类推,直到模式串P中的每个字符依次和主串T中的一个连续的字符序列相等,则称匹配成功,返回与P匹配的主串T的字符序列的第一个字符序号。否则称匹配失败,函数值为0(或-1)。简单模式匹配算法存在的问题:在模式匹配过程中存在已比较的字符重复比较。实际上已比较过的字符不必重复比较。

     1      int Index(SString T,SString P,int pos)
     2      {       
     3              int i,j,k;
     4              int m=strlen(P);
     5              int n=strlen(T);
     6              for(i=pos-1;i<n-m;i++)
     7              {  
     8                  for(j=0,k=i;j<m&&T[k]==P[j]; k++; j++);
     9                      if(j==m) reurn i;
    10               }
    11               return -1;
    12      }
    View Code

      2.3、KMP算法实现

       2.3.1、输入和输出
            输入:输入主串和模式串,以及开始比较的位置
            输出:输出模式串和主串开始匹配的起始位置,若不匹配则返回0,若匹配则返回匹配的位置。
       2.3.2、关键数据结构与算法描述
            数据结构:字符数组和整形数组
            算法描述:使用KMP算法,进行字符串匹配,最大的特点和优点就是i指针不回溯。首先要找到模式串对应的next数组的值。由于找到next数组是为了更好的进行匹配,因此再进行模式串与模式串的匹配求next数组时,可以对next数组进行优化,亦即如果t[i]==t[j],在进行i++,j++后如果t[i]==t[j],则next[i]=next[j];如果不相等,next[i]=j。求出next数组之后,就是KMP算法的主体了,要是j==-1或者s[i]==t[j],i,j都要加一,要不然j=next[j];继续进行比较,直至子串或主串结束。
         2.3.2.1、求next的算法

     1 void  get_nextval(char *t,int next[])
     2 {
     3     int i=0,j=-1;
     4     int size=strlen(t);
     5         next[0]=-1;
     6     while(i<size-1)
     7     {
     8         if(j==-1||t[i]==t[j])
     9         {
    10             i++;
    11             j++;
    12         
    13             if(t[i]==t[j])
    14             {
    15                 next[i]=next[j];
    16             }
    17             else
    18                 next[i]=j;
    19         }
    20         else
    21             j=next[j];
    22     }
    23 }
    View Code

         2.3.2.2、KMP算法主体

     1 int   KMP(char *s,char *p,int position,int next[])
     2 {
     3     int i=position-2;//position 为比较位置,从1开始读
     4     int j=-1;
     5     int S_SIZE=strlen(s),P_SIZE=strlen(p);
     6     while(i<S_SIZE&&j<P_SIZE)
     7     {
     8         if(j==-1||s[i]==p[j])
     9         {
    10             i++;
    11             j++;
    12         }
    13         else
    14             j=next[j];
    15     }
    16     if(j<P_SIZE)
    17         return 0;
    18     else
    19         return i-j+1;
    20 }
    View Code

      2.3.3、测试与理论
      1.主串:abcabdsfegabcdsdfg
       模式串:abdsfe
       起始位置:2,5
       理论结果:4,0
      2.主串:qqwerttryuriopazgdjcvjkfn
       模式串:ttryu
       起始位置:3,7
       理论结果:6,0

      2.3.4、所有代码

     1 #include"stdio.h"
     2 #include"string.h"
     3 void  get_nextval(char *t,int next[])
     4 {
     5     int i=0,j=-1;
     6     int size=strlen(t);
     7         next[0]=-1;
     8     while(i<size-1)
     9     {
    10         if(j==-1||t[i]==t[j])
    11         {
    12             i++;
    13             j++;
    14         
    15             if(t[i]==t[j])
    16             {
    17                 next[i]=next[j];
    18             }
    19             else
    20                 next[i]=j;
    21         }
    22         else
    23             j=next[j];
    24     }
    25 }
    26 
    27 int   KMP(char *s,char *p,int position,int next[])
    28 {
    29     int i=position-2;//position 为比较位置,从1开始读
    30     int j=-1;
    31     int S_SIZE=strlen(s),P_SIZE=strlen(p);
    32     while(i<S_SIZE&&j<P_SIZE)
    33     {
    34         if(j==-1||s[i]==p[j])
    35         {
    36             i++;
    37             j++;
    38         }
    39         else
    40             j=next[j];
    41     }
    42     if(j<P_SIZE)
    43         return 0;
    44     else
    45         return i-j+1;
    46 }
    47 
    48 int  main()
    49 {
    50   char  s[100];
    51   char  t[100];
    52   int  next[100],pos=1;
    53   while(1)
    54   {
    55 
    56      printf("请输入主串:
    ");
    57      scanf("%s",s);
    58      printf("请输入模式串:
    ");
    59      scanf("%s",t);
    60      printf("请输入开始比较的位数(默认为一)
    ");
    61      scanf("%d",&pos);
    62 
    63      get_nextval(t,next);
    64      printf("开始匹配的起始位置为(若为0则不匹配)
    ");
    65 
    66      printf("%d
    ",KMP(s,t,pos,next));
    67   }
    68    return 0;
    69 }
    View Code
  • 相关阅读:
    函数配接器
    函数对象和函数指针
    unary_function 和 binary_function
    堆排序
    Shell排序
    volatile理解
    死锁
    进程间通信
    优化循环的方法-循环展开
    如何学习编译原理
  • 原文地址:https://www.cnblogs.com/zyrblog/p/6869910.html
Copyright © 2011-2022 走看看