【转帖】字符串通配符

【前言】

    选题仍然是源自课堂习题,是要设计一个含有通配符问号的KMP字符匹配算法。其实这个目标是非常容易达到的。但是,接着想到通配符星号时,就感到有些困难了。因为星号可以匹配的字符个数不是确定的,所以子串中含有星号时要多次扫描母串,每种情况都要逐一比对才行,现有的匹配算法显然达不到这样的目的。现在要设计一种算法,即能完成不含通配符的匹配问题,同时又能适应含通配符的匹配问题。

【分析】

    通配符共有两种,问号和星号。“?”表示一个任意字符,而“*”则表示任意个任意字符。

    首先来想问号的处理,子串扫描到问号时,不管母串是什么字符我们都认为它们是匹配的就行了。就是在匹配条件中加一条,变成s[i]==t[j]||t[j]==’?’ (假设s是母串、t是子串),就行了。

    问题的重点是星号,当扫描到星号时,母串从当前位置之后的所有字符都要一一和子串的下一个字符比对,这样才能真正表述“*”的含义。显然,我们就要记住所有将要比对的位置,然后再一一处理,这就需要栈或者队列帮忙了。我就选择了队列。如果,接下来的处理中又遇到了“*”,同样入队。直到队列为空,所有的比对工作才真正完成了。

    为了协调各种情况,就统一采用只要需要比对就入队,比对前先出队的方法。这样就可以适应不管是否含通配符的多种情况了。这里入队的元素是字符串的下标。

【算法实现】

    程序依以下流程编写:1.用一个大循环来读取母串。2.先将量字符串的开头位置入队。3.用一个 while循环来读去队列元素,直到对空才停止。4.比对之前先出队元素。5.如果两位置上的字符相同(含字串字符是“?”),我们将各自的下一位置入 队。6.如果遇到“*”,则将母串从当前位置到结尾和子串的下一位置依次入队。7.以上两种情况中,如果子串读到了末尾,那么匹配成功,可以输出结果了。 8.如果字符不相同,不作任何处理;如果母串扫描完毕,子串还没有扫完,同样不作任何处理。

    还有一些细节需要设计:1.母串和子串的位置用一个结构存储,出队入队用这个结构操作。2.结果输出我采用了颜色标记的方法,另用函数实现。3.多个连续的“*”按一个处理。4.用s存储母串,用t存储子串,更改这两个字符串,多次运行。

【源代码】

    #include <stdio.h>

    #include <conio.h>

    #include <stdlib.h>

    typedef struct

    {

       int s,t;

    }STRINFO;

    typedef STRINFO Elemtype;

    typedef struct

    {

       Elemtype *Elem;

       int Front,Rear;

       int Size;

    }SQQUEUE;

     int InitSqqueue(SQQUEUE *q,int n)

    {

       q->Elem=(Elemtype*)malloc((n+1)*sizeof(Elemtype));

       if(q->Elem==NULL) return 0;

       q->Front=q->Rear=0;

       q->Size=n+1;

       return 1;

    }

    void DestroySqqueue(SQQUEUE *q)

    {

       free(q->Elem);

       q->Elem=NULL;

       q->Front=q->Rear=0;

     q->Size=0;

    }

    int IsSqqueueEmpty(SQQUEUE q)

    {

       return q.Front==q.Rear;

    }

    int IsSqqueueFull(SQQUEUE q)

    {

       return q.Front==(q.Rear+1)%q.Size;

    }

    int EnSqqueue(SQQUEUE *q,Elemtype e)

    {

       if(IsSqqueueFull(*q)) return 0;

       q->Elem[q->Rear]=e;

       q->Rear=(q->Rear+1)%q->Size;

       return 1;

    }

    int DeSqqueue(SQQUEUE *q,Elemtype *e)

    {

       if(IsSqqueueEmpty(*q)) return 0;

       *e=q->Elem[q->Front];

       q->Front=(q->Front+1)%q->Size;

       return 1;

    }

    void ShowStr(char *Str,int Begin,int End)

    {

       int i;

       textcolor(LIGHTGRAY);

       printf("From %2d To %2d : ",Begin,End);

       for(i=0;Str[i]!=0;i++)

       {

           if(i==Begin) textcolor(YELLOW);

           cprintf("%c",Str[i]);

           if(i==End) textcolor(LIGHTGRAY);

       }

       printf("\n");

    }

    void InStr(char *s,char *t)

    {

       int i=0,j;

       STRINFO Info;

       SQQUEUE q;

      InitSqqueue(&q,100);

       while(s[i]!=0)

       {

           Info.s=i;Info.t=0;

           EnSqqueue(&q,Info);

           while(!IsSqqueueEmpty(q))

           {

              DeSqqueue(&q,&Info);

              if(s[Info.s]==t[Info.t]||t[Info.t]=='?')

            {

                  if(t[Info.t+1]==0)

                  {

                     ShowStr(s,i,Info.s);

                  }

                 else

                  {

                     Info.s++;Info.t++;

                     if(s[Info.s]!=0) EnSqqueue(&q,Info);

                 }

              }

              else if(t[Info.t]=='*')

              {

                   while(t[Info.t]=='*')

                     Info.t++;

                  for(j=Info.s;s[j]!=0;j++)

                  {

                     if(t[Info.t]==0)

                         ShowStr(s,i,j);

                     else

                     {

                         Info.s=j;

                         EnSqqueue(&q,Info);

                     }

                  }

              }

           }

           i++;

       }

       DestroySqqueue(&q);

    }

    void main()

    {

       char s[80]="aabcdabcdabceab";

       char t[80]="a?b*c";

       clrscr();

       printf("Mother Str: %s\n",s);

       printf(" Child Str: %s\n",t);

       InStr(s,t);

    }

    这里是完整版,复制下来就可以直接运行了。下面给出一些测试用例,可以检测这个算法。

    Mother Str : aabcdabcdabceab

     Child Str : a?b*c

     Child Str : abc?

     Child Str : a*a*a

     Child Str : ???

     Child Str : **

     Child Str : a*

     Child Str : *b

     Child Str : abcdabce

【总结】

    其实,这个程序是含有通配符的简单字符串匹配的算法。至于含有“?”的KMP算法,该一个匹配条件就行 了。而含有“*”的KMP是求不出Next值的。譬如说:a*abcd,c的Next是什么呢?如果“*”表示空白,其值为0;如果“*”表示be,其值 应该是e的下标而不是b的下标,但是be都是一个下标,算那个呢?含有“*”的子串求不出Next值。

posted on 2011-10-13 10:17  白草黒尖  阅读(2278)  评论(0)    收藏  举报