3.2 Implement strStr()

来源:互联网 发布:巅峰阁软件帐号 编辑:程序博客网 时间:2024/06/07 02:38

模式匹配(模范匹配):子串在主串中的定位称为模式匹配或串匹配(字符串匹配) 。模式匹配成功是指在主串S中能够找到模式串T,否则,称模式串T在主串S中不存在。

方法一

1.Brute-Force模式匹配算法(暴力匹配法)

设S为目标串,T为模式串,且不妨设:S=“s0 s1 s2…sn-1” , T=“t0 t1 t2 …tm-1”

       串的匹配实际上是对合法的位置0≦i≦n-m依次将目标串中的子串s[i…i+m-1]和模式串t[0…m-1]进行比较:

 若s[i…i+m-1]=t[0…m-1]:则称从位置i开始的匹配成功,亦称模式t在目标s中出现;

 若s[i…i+m-1]≠t[0…m-1]:从i开始的匹配失败。位置i称为位移,当s[i…i+m-1]=t[0…m-1]时,i称为有效位移;当s[i…i+m-1]≠t[0…m-1]时,i称为无效位移。

2.Brute-Force算法思想:

从主串S的第i个字符开始与子串T的第一个字符比较,如果相等则继续比较后续字符;否则从主串S的下一字符开始与子串T的的第一个字符比较,依次类推。匹配成功返回T中第一个字符在主串S中的位置,否则返回-1.



*主串总是从已匹配字符子串第一个字符的下一个字符开始(i-j+1),子串从第一个字符开始,两者进行比较,即S[i-j+1]和T[0]比较。

3.代码

class Solution{

public:

char *strStr(const char *S,int pos,const char* T){  //从S的第pos位置开始查找子串T;

if(!*T) return (char*)S;                                        //子串T若为空;

int i,j;                                                                 //i标示主串中比较字符下标,j标示子串中比较字符下标

i=pos-1;                                                              //pos表示第几个字符,从1开始;i是下标,从0开始

j=0;

while(i<strlen(S)&&j<strlen(T))

{

        if(S[i]==T[j])  

{i++;j++}

else

{i=i-j+1;j=0}

}

if(j>=strlen(T))  

return i-(j-1);

                else    

return  -1;    

       }

}


方法二

1.KMP算法

此方法使主串指针不回退,只增长。子串指针尽量后移,而不是像方法一中的总是从第一个元素开始。

2.KMP算法思想

在字符串S中寻找T,当匹配到位置i时两个字符串不相等,这时我们需要将字符串T向前移动。常规方法是每次向前移动一位,但是它没有考虑前i-1位已经比较过这个事实,所以效率不高。事实上,如果我们提前计算某些信息,就有可能一次前移多位。假设i之前的字符串(T的子串)为f

f有如下特点:

  • A段字符串是f的一个前缀。
  • B段字符串是f的一个后缀。
  • A段字符串和B段字符串相等且长度为L。

所以前移k位之后,可以继续比较位置i的前提是T的前i-1个位置满足:长度为L=i-k-1的前缀A和后缀B相同。只有这样,我们才可以前移k位后从新的位置继续比较。前移只是一种形象的说法,在实际比较过程中是在T的最大公共长度之后进行再次比较。

next 思想见博客http://blog.csdn.net/yutianzuijin/article/details/11954939

3.next函数

当主串i位置和子串j位置字符不匹配时,j要进行跳转,也就是指示子串比较位置的j要发生变化.


在求得了next[j]值之后,KMP算法的思想是:

    设目标串(主串)为s,模式串为t ,并设i指针和j指针分别指示目标串和模式串中正待比较的字符,设i和j的初值均为1。若有si=tj,则i和j分别加1。否则,i不变,j退回到j=next[j]的位置,再比较si和tj,若相等,则i和j分别加1。否则,i不变,j再次退回到j=next[j]的位置,依此类推。直到下列两种可能:

(1)  j退回到某个下一个[j]值时字符比较相等,则指针各自加1继续进行匹配。

(2)退回到j=-1,将i和j分别加1,即从主串的下一个字符s[i+1]模式串的t[0]重新开始匹配。

KMP算法如下:

#define Max_Strlen 1024

int next[Max_Strlen];

int KMP_index (const char* S ,const char *T)

 /* KMP算法进行模式匹配,匹配返回位置,否则返回-1*/

  /*用静态存储方式保存字符串,st分别表示主串和模式串  */

{  int  i=0 , j=0 ;     /*初始匹配位置设置 */

while( (i<strlen(S))&&(j<strlen(T)))

{ if ((j==-1)|| (S[i]==T[j]))  {  k++ ; j++ ; }

   else j=next[j] ;

}

if (j>= strlen(T)) return(k-t.length) ;

else return(-1) ;

}    


上述代码是在已知next的情况下写的,下面求next函数:用归纳法:

求模式串的next[j]值与主串s无关,只与模式串t本身的构成有关,next函数定义可知:

1)当j=1时:next[1]=0; 当j=0,next[0]=-1;

2)设next[j]=k,即在模式串中存在:t1t2tk-1=tj-(k-1)tj-k tj-1,其中下标k满足1<k<j的某个最大值,

3)此时求next[j+1]的值有两种可能:

 

该图中i处不匹配时i跳转到next[i]

(1)若位置i和位置next[i]处的两个字符相同(下标从零开始),则next[i+1]等于next[i]加1。即next[i+1]=next[i]+1=k+1;

(2)如果两个位置的字符不相同,我们可以将长度为next[i]的字符串继续分割,获得其最大公共长度next[next[i]],然后再和位置i的字符比较。这是因为长度为next[i]前缀和后缀都可以分割成上部的构造,如果位置next[next[i]]和位置i的字符相同,则next[i+1]就等于next[next[i]]加1。如果不相等,就可以继续分割长度为next[next[i]]的字符串,直到next[0]=-1为止。(递推法)

小结next求法:

next[0]=-1;

假设next[j]=k,则表示已找到j以前的最大重叠子串,T[0]~T[k-1];

当j和next[j]位置处的元素相等时:next[j+1]=next[j]+1=k+1;

当j和next[j]位置处的元素不相等时:next[j+1]=next[...next[next[j]]]+1;一定要找到一个位置next[...next[next[j]]],此位置的元素与j位置处元素相同,且此位置之前的元素与j位置之前的部分元素能够匹配上。


根据上述分析, 求next函数值的算法如下:

 /*  求模式串Tnext函数值并保存在next数组中  */

void next(const char *T ,int next[] )
{int  j=0 ,k=-1;

next[0]=-1;

while (j<strlen(T))

{  if ((k==-1)|| (T[k]==T[j]))

    {  

 k++ ; 

 j++ ;

 if ( T[k]!=T[j] )  next[j]=k;                       //因为假设j+1位置处匹配失败了才求的next[j+1],之前的j+1是现在的j(有                        else                     next[j]=next[k];             /++操作),如果跳转到k后的位置与j处元素相等,则还是会在与主串匹配过                                                                                       //程中失败,则还要继续跳转,也就是就next值,一直要找到最终那个不会                                                                                        //再跳转的位置

    }

 else  k=next[k] ; 

}

}    



 

0 0
原创粉丝点击