kmp算法,poj1226示例
kmp算法的主要作用:
给出两个字符串str1,str2
str1: a c a c b a c b a b c a
str2: a c b a b
利用kmp算法能够求出str2在str1中首次出现时的位置。
kmp算法的原理:
普通暴力方法求出子串首次出现的位置算法复杂度为O(nm),而利用kmp可以将复杂度降到O(n+m)
kmp到底什么地方神奇呢?它最神奇的地方在于它能够利用已经匹配成功的成果
第一趟匹配
str1: a c a c b a c b a b c a
str2: a c b a b
第二趟匹配
str1: a c a c b a c b a b c a
str2: a c b a b
第三趟匹配
str1: a c a c b a c b a b c a
str2: a c b a b
关键在于第二趟匹配失败后将str2向后移动了多少
这时候kmp算法的作用就出来了,很容易发现str2向后移动多少跟串str1是没有关系的
所以str2自身就可以确定比较某个字符失败后向右移动多少位置
我们用一个数组next[]来记录str2的每一个字符匹配失败后需要向右的位移(也就是从某个下标重新比较)
索引j 1 2 3 4 5
str2 a c b a b
next[j] 0 1 1 1 2
这个next是如何确定的呢?
用通俗一点的话来解释:
当j == 1时 如果a与某个字符串的字符不相等,则j = next[j] 也就是j = 0;
当j == 2时 如果c与某个字符串的字符不相等,则j = next[j] 也就是j = 1;
当j == 3... ......j = 1;
当j == 4... ......j = 1;
当j == 5... ......j = 2;
我们发现当比较到str2[j]的时候 str2[j]前面n个元素如果跟str2[0]后的n个元素相同,那么next[j]+=n;
这个如何解释呢,让我们来看一个例子:
如果str2为abcabcdeaf
当比较到红色的d的时候它的前三个字符abc与str2起始的三个字符abc相同
那么当它与某个字符串的某个字符匹配失败的时候说明str2到d为止前面的字符全都比较成功了
那么我们下次比较就可以不用比较相同的abc了直接从下一个也就是第四个元素a来对应原字符串就可以了
举个例子:
当比较
str1 abcabc a bcdef
str2 abcabc d ef
时e和d不相同,可是前面的部分abcabc都相同,那么我们就可以往下这样进行
str1 abcabc e bcdef
str2 abc a bcdef
这样我相信大家都能够理解了吧。
那么用编程的方式怎么来实现求出某个字符串的next[]数组呢?
我用c语言做一个例子:
接下来我们只需要将str1和str2进行比较,如果有字符不相同就向后移动str2就可以了
具体的算法描述为:
接下来我们用kmp算法解决poj上1226题
题目大意:找出n个串中最大的公共子串长度
#include <stdio.h>
#include <string.h>
#define Maxn 101
char str[Maxn][Maxn];
int next[Maxn],rnext[Maxn];
int T,n;
void get_next(int s,int e,int flag)
{
int i,j;
if(flag == 1)
{
next[s] = s - 1;
i=s,j=s-1;
while (i <= e) {
if (j == s-1 || str[0][i] == str[0][j]) {
i++;j++;next[i]=j;
}
else
{
j = next[j];
}
}
}
else
{
rnext[e]= e+1;
i=e,j=e+1;
while (i >= s) {
if (j == e+1 || str[0][i] == str[0][j]) {
i--;j--;rnext[i]=j;
}
else
{
j = rnext[j];
}
}
}
}
int kmp(int k,int s,int e,int flag)
{
int i,j,len;
len = strlen(str[k]);
if(flag == 1)
{
for(i = 0, j = s; i < len && j <= e;)
{
if(j == s - 1 || str[k][i] == str[0][j])
i++, j++;
else
j = next[j];
}
if(j > e)
return 1;
}
else
{
for(i = 0, j = e; i < len && j >= s;)
{
if(j == e + 1 || str[k][i] == str[0][j])
i++, j--;
else
j = rnext[j];
}
if(j < s)
return 1;
}
return 0;
}
int main()
{
scanf("%d",&T);
while (T--!=0) {
scanf("%d",&n);
for (int i = 0; i < n; i++) {
scanf("%s",str[i]);
}
int len = (int)strlen(str[0]);
int ans = 0;
for (int i = 0; i < len; i++) {
int l;
for (int j = i; j < len; j++) {
get_next(i, j, 1);
get_next(i, j, 0);
l = j - i + 1;
int cnt=1;
for (int k = 1; k < n; k++) {
if (kmp(k, i, j, 1) ==1|| kmp(k, i, j, 0)==1) {
cnt++;
}
else
break;
}
if (cnt == n&& ans < l) {
ans = l;
}
}
}
printf("%d\n",ans);
}
return 0;
}