前几天学习了关于字符串处理的KMP算法,刚学的时候没怎么懂,通过今天的练题,终于把KMP掌握了。
KMP算法利用了字符串的一些特殊性质,通过前缀数组,将单个字符串的匹配问题由 O(m∗n) 优化到了 O(m+n) 。
这里,我存几段关键性代码。
首先是get_next()函数
void get_next()
{
int i=0,j=-1;
next[0] = -1;
while(i != m)
{
if(j==-1||a[i] == a[j])
next[++i] = ++j;
else j = next[j];
}
}
然后是KMP()
void KMP()
{
int cnt = 0;
int i = 0,j = 0;
while(i!=n&&j!=m){// m表示子串长度
if(t[i] != a[j]&&j!=-1){
j=next[j];
}
else {i++;j++;}
if(j == m){
cnt++;
j = next[j];
}
}
printf("%d\n",cnt);//这是求子串个数
}
void KMP()
{
int i = 0,j = 0;
int ans = -1;
while(i != n && j != m){
if(a[i] == b[j] || j == -1){
++i,++j;
}
else j = next[j];
if(j == m){
ans = i+1-m;break;
}
}
printf("%d\n",ans);//这是求第一次匹配的下标,ans = -1表示不能匹配
}
还有一类奇怪的问题,询问一段字符串的循环节,这就只需要在get_next()上改一点就好了
//-----poj2406-----这是求整个字符串的循环节个数
int i = 0,j = -1;
next[0] = -1;
while(i != len){
if(j == -1 || s[i] == s[j])
next[++i] = ++j;
else j = next[j];
}
int ans = 1;
if(len % (len - next[len]) == 0){
ans = len / (len - next[len]);
}
printf("%d\n",ans);
//-----poj1961-----这是求前i个字符中循环节的个数
printf("Test case #%d\n",++t);
int i = 0,j = -1;
next[0] = -1;
while(i != len){
if(j == -1 || s[i] == s[j]){
next[++i] = ++j;
if(i % (i - next[i]) == 0){
int k = i / (i - next[i]);
if(k>1)printf("%d %d\n",i,k);
}
}
else j = next[j];
}
printf("\n");
留题备忘(附题解)