字符串的定位操作通常称为串的模式匹配。模式匹配的应用很常见的函数模式一般是:
int index(const string &Tag, const string &Ptn, int pos)
其中,Tag是主字符串,Ptn是子字符串,如果在主串Tag的第pos个位置后存在与子字符串Ptn相同的子串,返回它在主串第pos个字符后第一次出现的位置,否则返回-1。
一、BF算法:
暴风(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。如图所示:
int index(const string &Tag, const string &Ptn, int pos) { int i = pos;//主串当前要比较的位置,初始化为pos int j = 0;//子串当前要比较的位置,初始化为0 int Tlen = Tag.size();//主字符串的长度 int Plen = Ptn.size();//子字符串的长度 while (i < Tlen && j < Plen) { if (Tag[i] == Ptn[j])//如果当前字符串相同,则继续向下比较 { i++; j++; } else//如果不同,把i和j退回到初始位置,重新进行比较 { i = i - j + 1; j = 0; } } if (j >= Plen)//如果比较的长度已经大于子串长度,说明已经匹配成功 { return (i - Plen); } else { return -1; } }
二、KMP算法:
BF算法的时间复杂度最坏情况是o(Plen*Tlen),而之所以时间复杂度是由于索引指针的回溯引起的,针对以上不足,于是出现了KMP算法。它的时间复杂度为o(Plen+Tlen)。改进之处在于:每一趟比较重出现的字符不等时,不需要回溯索引指针i,而是利用已经得到的部分匹配结果将子串向右滑动尽可能远的距离,继续进行比较。
如果采用简单的BF算法,则每趟比较i都会要退回,而采用KMP算法,每趟比较时,保持不变,只需要将j向右滑动即可,可即减少了中间一些趟次的比较。
//求next数组中各元素的值,保存在长为len的next数组中
void get_next(const string &Ptn, int *next, int len)
{
int j = 0;
int k = -1;
next[0] = -1;
while (j<len - 1)
{
if (k == -1 || Ptn[j] == Ptn[k])
{ //如果满足上面分析的Pk = Pj的情况,则继续比较下一个字符,
//并得next[j+1]=next[j]+1
j++;
k++;
next[j] = k;
}
else
{ //如果符合上面分析的第2种情况,则依据next[k]继续寻找下一个比较的位置
k = next[k];
}
}
}
//求next数组的改进数组中各元素的值,保存在长为len的nextval数组中
void get_nextval(const string &Ptn, int *nextval, int len)
{
int j = 0;
int k = -1;
nextval[0] = -1;
while (j<len - 1)
{
if (k == -1 || Ptn[j] == Ptn[k])
{ //如果满足上面分析的Pk = Pj的情况,则继续比较下一个字符,
//并得next[j+1]=next[j]+1
j++;
k++;
if (Ptn[j] != Ptn[k])
nextval[j] = k;
else //Ptn[j]与Ptn[k]相等时,直接跳到netval[k]
nextval[j] = nextval[k];
}
else
{ //如果符合上面分析的第2种情况,则依据next[k]继续寻找下一个比较的位置
k = nextval[k];
}
}
}
/*
返回子串Ptn在主串Tag的第pos个字符后(含第pos个位置)第一次出现的位置,若不存在,则返回-1
采用KMP算法,这里的位置全部以从0开始计算为准,其中T非空,0<=pos<=Tlen
*/
int kmp_index(const string &Tag, const string &Ptn, int pos)
{
int i = pos; //主串当前正待比较的位置,初始为pos
int j = 0; //子串当前正待比较的位置,初始为0
int Tlen = Tag.size(); //主串长度
int Plen = Ptn.size(); //子串长度
//求next数组的值,并逐个输出
int *next = (int *)malloc(Plen * sizeof(int));
get_next(Ptn, next, Plen);
// get_nextval(Ptn,next,Pln);
int t;
cout << "子串的next数组中的各元素为:";
for (t = 0; t<Plen; t++)
cout << next[t] << " ";
cout << endl;
while (i<Tlen && j<Plen)
{
if (j == -1 || Tag[i] == Ptn[j])
{ //如果当前字符相同,或者在子串的第一个字符处失配,则继续向下比较
i++;
j++;
}
else //如果当前字符不同,则i保持不变,j变为下一个开始比较的位置
{
//next数组时KMP算法的关键,i不回退,
//而是继续与子串中的next[j]位置的字符进行比较
j = next[j];
}
}
if (j >= Plen)
return (i - Plen);
else
return -1;
}
(没有写好,有时间再写,未完待续!)