【前言】
    选题仍然是源自课堂习题,是要设计一个含有通配符问号的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值。