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;

}

 

posted @ 2014-06-23 15:04  蜗牛强  阅读(215)  评论(0编辑  收藏  举报