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*/
/*用静态存储方式保存字符串,s和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函数:用归纳法:
求模式串的next[j]值与主串s无关,只与模式串t本身的构成有关,由next函数定义可知:
1)当j=1时:next[1]=0; 当j=0,next[0]=-1;
2)设next[j]=k,即在模式串中存在:t1t2…tk-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函数值的算法如下:
/* 求模式串T的next函数值并保存在next数组中 */
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] ;
}
}
- 3.2 Implement strStr()
- 【leetcode】3.2 Implement strStr()
- 3.2 Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- Implement strStr()
- 2429: [HAOI2006]聪明的猴子 最小生成树
- mycat 主备同步 读写分离注意事项
- intelliJ IDEA 创建自定义模板 用于版权信息和自定义标签
- C++11内存模型
- AFNetWorking Post请求 及 刷新网址 经典十大算法
- 3.2 Implement strStr()
- average presion(AP)
- Struts2 Action扩展名的三种修改方法
- PE文件详解(一)--数据结构字段
- TIP 链接错误 link error
- Linux下使用gcc和gdb调试程序
- 安卓自定义进度条
- 初探linux内核编程,参数传递以及模块间函数调用
- OPUS/SILK/SPEEX 音频编码比较