昨天又看了一天的kmp的博客,结果是一直在套着模板做题,完全没理解,简单的模板题很容易A,但稍微考察思想的题就看不懂了,到了晚上偶然翻了翻蓝书,感觉弄懂了点。
下面是分析过程:思路还是有点乱,也只能是自己看,,,再沉淀一下应该会理解的更好。
KMP:
解决的问题:字符串的匹配问题;
分析:
假设现在有两个字符串:母串ababbbabac,模板串abac;问题时查找模板串在母串中出现的位置;
暴力解法:
1、将母串以及模板串的首端对齐;
2、模板串长度范围内逐个对比母串与模板串的元素;
3、若完全一样,匹配成功;否则,匹配失败;
4、将模板串首端与母串第二位对齐,重复过程2、3,直至模板串首位与母串最后一个元素对齐;
优化:
例如上例,当母串的abab与模板串的abac匹配时,我们发现最后一个元素不一致,所以此时按照暴力解法,我们应该将模板串的首位与母串的第二位对齐,再重复搜索(暴力),但根据实际情况,我们知道上述移位操作是毫无意义的,有意义且正常的操作是将模板串的首位与母串的第三位对齐,再继续遍历,同理,下次应当将模板串首位与母串倒数第四为对齐;所以,我们要找一种方法,得到 正常思考 中模板串首位应当移动的位置。
容易发现,这种正常思考中的移动,其实就是去寻找模板串与母串已经匹配的部分(模板串的前多少位)的模板串的前缀(这里不含最后一位)与母串后缀(这里不含前缀)的最大匹配值,即将对应匹配值的前缀与后缀对齐,例如这里已经匹配的部分为aba,母串后缀ba与模板串前缀ab最大匹配值为1,所以将模板串的a与母串的a对齐,即模板串的首位与母串的第三位对齐;
所以现在问题变为怎么求匹配值,即求next数组,不妨将模板串自身与自身比较,当产生一段匹配区间时,(例如串2的i~对应长度位置与串1的1~j已经一一对应,则next【串1当前的末位置】=j),所以next数组就求出来了。 匹配过程中模板串的移动还和上段思路一样。
用代码实现的话就是
int next[11000];
void getNext()
{
next[1]=0;//字符串数组从1开始;
for(int i=2,j=0;i<=strlen(str);++i)
{
//j>0,出现第一个匹配的字符时才开始算法,否则说明除了两串相同外没有其他串1后缀与串2前缀匹配的部分
while(j>0&&str[i]!=str[j+1])j=next[j];//某一位不对应,调整模板串的位置,只用到移动后对应完成时的后一位j;
if(str[j+1]==str[i])j++;
next[i]=j;
}
}
同理,我们也可以得到模板串与母串匹配的代码;
int kmp[11000];
void KMP()
{
//kmp[1]=0;两串完全相同也算一种情况,所以开头不再处理;
for(int i=1,j=0;i<=n;++i)//n是母串长度;str母串,ptr模板串;
{
while(j>0&&str[i]!=ptr[j+1])j=next[j];
if(str[i]==ptr[j+1])j++;
kmp[i]=j;
// 题型1:if(j==m)//m是模板串的长度,j==m说明母串中包含模板串,若秋第一次出现位置,返回i-m+1即可.求所有出现位置就只输出结果,不返回,继续下一层循环。
//题型2:求出现次数,定义一个sum即可,继续下一层循环.
}
}
针对问题的话:
目前看了的比如求模板串在母串中出现的位置,次数,这是最基础的模板题;
变式的话比如求多个串的最长公共子序列,这时枚举第一个串的子串,然后与其他串匹配即可,注意不要写成暴力,kmp这里没理解好的话是会TLE的,例如poj3450:http://poj.org/problem?id=3450。