子串的定位运算通常称为串的模式匹配或串匹配。此运算的应用非常广泛,比如在搜索引擎、拼写检查、语言翻译、数据压缩等应用中,都需要进行串匹配。
设有两个字符串S和T,设S为主串,也称正文串;设T为子串,也称为模式串·。在主串S中找与模式T相匹配的子串,如果匹配成功,确定相匹配的子串中的第一个字符在主串S中出现的位置。
1.BF算法
BF算法,即暴力算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得到最后的匹配结果。
【算法步骤】
1.分别利用计数指针i和j指示主串S和模式T中当前正待比较的字符串位置,i和j初值为1。
2.如果两个串均未比较到串尾,即i和j均分别小于等于S和T的长度时,则循环执行以下操作:
- S.ch[i]和T.ch[j]比较,若相等,则j和i分别指示串中的下一个位置,继续比较后面的字符;
- 若不相等,指针后退重新开始匹配,从主串的下一个字符(i=i-j+2)起再重新与模式的第一个字符(j=1)比较。
3.如果j>T.length,说明模式T中的每一个字符依次和主串S中的一个连续的字符序列相等,则匹配成功。
【算法描述】
int Index_BF(string S, string T)
{
int i,j;
i = 1, j= 1;
while(i<=S.length && j<=T.length)
{
if(S.ch[i] == T.ch[j]){i++;j++;}
else{i=i-j+2;j=1;}
}
if(j > T.length) return i-T.length; //匹配成功
else return 0;
}
2.KMP算法
这种改进算法是由克努特(Knuth)、莫里斯(Morris)和普拉特(Pratt)共同设计实现的,因此简称KMP算法。此算法可以在O(n + m)的时间数量级上完成串的模式匹配操作。其改进在于:每当一趟匹配过程中出现字符不等时,无须回溯主串的指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的一段距离后,继续进行比较。下面先从具体例子看起。
如图,在第一趟匹配中,第一个字符a和第二个字符b都匹配,而到第三个字符时,我们发现两个串中的字符不在匹配,所以这时,我们首先要找从不匹配字符的前面的字符是否出现前后缀(前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串)相等,很明显没有出现前后缀相等,那么i值(i=3)保持该位置,而j回到初值。
在第二趟匹配中,前四个字符a,b,c,a匹配,而到第五个字符时不再匹配,那么就判断前四个字符是否存在前后缀相同,很明显,第一个字符a和第四个字符a相同,那么,i值(i=7)依旧保持该位置,而j则移动到前缀的下一个位置(j=2)。
而到第三次匹配中,模式串的字符和主串字符都相互匹配,那么则匹配成功。
在这三次匹配中,重点是要确定模式串的下一个位置会移动到哪里,那么就引入了另一个知识点:next数组。
next数组在这不会讲的很详细。
1.确定next数组的第一、二位一定分别是0、1,后面求解每一位的next值时,根据前一位进行比较。
2.从第三位开始,将前一位与其next值对应的内容进行比较,如果相等,则该位的next值就是前一位的next值➕1;如果不相等,向前继续寻找next值对应的内容与前一位进行比较,直到找到某位上的内容的next值对应的内容与前一位相等为止,则这个位对应的值➕1,即为需求的next值;
如果找到第一位都没有找到与前一位相等的内容,那么next值为1。
【模式串T的next数组算法】
void get_next(string T, int next[])
{
int i=1,j=0;
next[1]=0;
while(i<T.length)
{
if(j==0 || T.ch[i] == t.ch[j])
{
i++;j++;
next[i] = j;
}
else j=next[j];
}
}
【KMP算法】
int Index_KMP(string S, string T)
{
int i=1,j=1;
int next[T.length+1];
get_next(T, next);//求模式串的next数组
while(i<=S.length && j<=T.length)
{
if(j==0 || S.ch[i] == T.ch[j])
{
i++;j++;
}
else j=next[j];
}
if(j>T.length) return i-T.length;
else return 0;
}