近日看到一面试题写一个函数从字符串s中查找出子串t,并将t从s中删除。
这是考查KMP匹配算法,以前读书时学KMP总是一知半解,考完试基本都还给老师了,
唯一的印象就是KMP实现了匹配字符串指针不回溯,极大的提高了效率,
最近代码写的少拿此题练练手。
考虑当第k个字符失配的时候,sm+k+1≠tk+1时,有如下关系
sm+1sm+2…sm+k = t1t2…tk
下一次再比较时,指针不回溯,应该比较sm+k+1和t的哪个字符呢?
假设应该和ti比较,那么有如下关系
t1t2…ti-1 = sm+k-i+2sm+k-i+3…sm+k (i-1<k)
t1t2…ti-1 = tk-i+2tk-i+3…tk (i-1<k),这是字符串t自身的匹配关系,
当然这样的i可能不止一个,next[i]就是所有这样i里面的最大值,
i越大也意味着满足t1t2…ti-1 = tk-i+2tk-i+3…tk关系的k-i+2越小,
因此下一个需要比较的就是满足上述条件i的最大值,失配后t往右移动的距离最小,
字符串t第i个字符失配后sm+k+1应该和t的next[i]个字符继续比较。
举个具体例子s=“ababababababc“, t=“abababc“当s[6]=a与t[6]=c失配时,
s[6]应该和t[4]=a继续比较,再失配和t[2]=a比较,而不能先和t[2]=a比较,
这样如果原始字符串s=“ababababc“结果就不对了。
有了上述推理,不难写出匹配代码:
i,j分别指向s,t中某个字符,一直匹配,直到某字符失配后,j=next[j]继续比较,
如果next[j]=-1,表示第一个字符就不匹配,此时++i, 比较s的下一个字符,
tlen-j是字符串中待比较字符个数,循环退出条件是s中剩余字符小于t中待比较字符数。
while(i<=slen-tlen+j){
if(s[i] == t[j]){
++i;
++j;
//find match string, erase it
if ( j == tlen){
strcpy(s+i-tlen, s+i);
slen -= tlen;
i -= tlen;
j = 0;
}
}
else{
if ( (j = next[j]) == -1 ){
++i;
j = 0;
}
}
}