字符串匹配

模式匹配(模范匹配):
子串在主串中的定位称为模式匹配或串匹配(字符串匹配) 。

方法一 Brute-Force模式匹配算法(暴力匹配法)

  1. 算法思想
    s:目标串,sub:模式串
    s=“s0 s1 s2…sn-1”
    sub=“t0 t1 t2 …tm-1”
    从主串s的第i个字符开始与子串sub的第一个字符比较,如果相等则继续比较后续字符;否则从主串s的下一字符开始与子串sub的的第一个字符比较,依次类推。
    匹配成功返回sub中第一个字符在主串s中的位置,否则返回-1.

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

 //从S的第pos位置开始查找子串T;
class Solution{
public:
    char *strStr(const char *S,int pos,const char* T){                   
    if(!*T) 
        return (char*)S;                                         
    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;    
    }
}

方法二 KMP算法

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

在字符串s中寻找sub,当匹配到位置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.youkuaiyun.com/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算法如下:

//用KMP算法进行模式匹配,匹配返回位置,否则返回-1
//用静态方式保存字符串,s和t分别表示主串和模式串  
#define Max_Strlen 1024
int next[Max_Strlen];
int KMP_index (const char* S ,const char *T)
{  
    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[j]值只与模式串sub有关,由next函数定义可知:
(1)当j=1时,next[1]=0; 当j=0,next[0]=-1;
(2)设next[j]=k,即在模式串中存在:
t[1…k-1]=t[j-(k- 1)… j-1],其中k在1和j之间的某个最大值,
(3)此时求next[j+1]的值有两种可能:

a.该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以前的最大重叠子串,sub[0]~sub[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位置之前的部分元素能够匹配上。

 /*  求模式串Tnext函数值并保存在next数组中  */
/*因为假设j+1位置处匹配失败了才求的next[j+1],之前的j+1是现在的j(有++操作),如果跳转到k后的位置与j处元素相等,则还是会在与主串匹配过程中失败,则还要继续跳转,也就是就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;                                            
            else 
                next[j]=next[k];        
        }
        else  k=next[k] ; 
    }
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值