HDU 1711 Number Sequence (KMP)

来源:互联网 发布:网盘网站源码 编辑:程序博客网 时间:2024/05/17 02:10
Problem Description
Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], ...... , b[M] (1 <= M <= 10000, 1 <= N <= 1000000). Your task is to find a number K which make a[K] = b[1], a[K + 1] = b[2], ...... , a[K + M - 1] = b[M]. If there are more than one K exist, output the smallest one.
 

Input
The first line of input is a number T which indicate the number of cases. Each case contains three lines. The first line is two numbers N and M (1 <= M <= 10000, 1 <= N <= 1000000). The second line contains N integers which indicate a[1], a[2], ...... , a[N]. The third line contains M integers which indicate b[1], b[2], ...... , b[M]. All integers are in the range of [-1000000, 1000000].
 

Output
For each test case, you should output one line which only contain K described above. If no such K exists, output -1 instead.
 

Sample Input
213 51 2 1 2 3 1 2 3 1 3 2 1 21 2 3 1 313 51 2 1 2 3 1 2 3 1 3 2 1 21 2 3 2 1
 

Sample Output
6-1
#include<iostream>#include<cstdio>using namespace std;int nextt[10010],a[1000010],b[10010];int m,n;void get_next(){    int k=0,j=1;    nextt[0]=-1;    nextt[1]=0;    while(j<m-1)    {        if(k==-1 || b[k]==b[j])        {            k++;            j++;            nextt[j]=k;        }        else            k=nextt[k];    }}int kmp(){    int i=0,j=0;    while(i!=n && j!=m)    {        if(j==-1 || a[i]==b[j])        {            i++;            j++;        }        else            j=nextt[j];    }    if(j==m)        return i-j+1;    else        return -1;}int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d %d",&n,&m);        for(int i=0;i<n;i++) scanf("%d",&a[i]);        for(int i=0;i<m;i++) scanf("%d",&b[i]);        get_next();        int ans=kmp();        printf("%d\n",ans);    }    return 0;}

下面讨论在一般性的情况下,如何实现在“不回朔”访问S、仅依靠“滑动”P的前提下实现字符串匹配,即“kmp算法”

                               i=6

S: a   b   a   b   c   a   d   c   a   c   b   a   b

P:                        a   b   c   a   c

                              k=1

                                i=6

S: a   b   a   b   c   a   d   c   a   c   b   a   b

P:           a   b   c   a   c

                               j=4

对于任意的S和P,当S中index为 i 的字符和P中index为 j 的字符失配时,我们假定应当滑动P使其index为 k 的字符与S中index为 i 的字符“对齐”并继续比较。

那么,这个 k 是多少?

我们知道,所谓的对齐,就是要让S和P满足以下条件(上图中的蓝色字符):

……(1)

另一方面,在失配时我们已经有了一些部分匹配结果(上图中的绿色字符):

……(2)

由(1)、(2)可以得到:

……(3)

即如下图所示效果:

定义next[j]=k,k表示当模式串P中index为 j 的字符与主串S中index为 i 的字符发生失配时,应将P中index为 k 的字符继续与主串S中index为 i 的字符比较。

……(4)

按上述定义给出next数组的一个例子:

   j         0  1  2  3  4  5  6  7

   P        a   b  a  a  b  c  a   c

next[j]  -1  0  0  1  1  2  0  1

在已知next数组的前提下,字符串匹配的步骤如下:

i 和 j 分别表示在主串S和模式串P中当前正待比较的字符的index,i 的初始值为sIndex,j 的初始值为0。

在匹配过程中的每一次循环,若,i 和 j 分别增 1,

else,j 退回到 next[j]的位置,此时下一次循环是相比较。

4、kmp算法的实现

 在已知next函数的前提下,根据上面的步骤,kmp算法的实现如下: 

复制代码
int kmp(const std::string& s, const std::string& p, const int sIndex = 0){    std::vector<int>next(p.size());    getNext(p, next);//获取next数组,保存到vector中    int i = sIndex, j = 0;    while(i != s.length() && j != p.length())    {        if (j == -1 || s[i] == p[j])        {            ++i;            ++j;        }        else        {            j = next[j];        }    }    return j == p.length() ? i - j: -1;}
复制代码

ok,下面的问题是怎么求模式串 P 的next数组。

next数组的初始条件是next[0] = -1,设next[j] = k,则有:

那么,next[j+1]有两种情况:

,则有:

   此时next[j+1] = next[j] + 1 = k + 1

, 如图所示:

此时需要将P向右滑动之后继续比较P中index为 j 的字符与index为 next[k] 的字符:

 

值得注意的是,上面的“向右滑动”本身就是一个kmp在失配情况下的滑动过程,将这个过程看 P 的自我匹配,则有:

如果,则next[j+1] = next[k] + 1;

否则,继续将 P 向右滑动,直至匹配成功,或者不存在这样的匹配,此时next[j+1] = 0。

 getNext函数的实现如下:

复制代码
void getNext(const std::string &p, std::vector<int> &next){    next.resize(p.size());    next[0] = -1;    int i = 0, j = -1;        while (i != p.size() - 1)    {        //这里注意,i==0的时候实际上求的是next[1]的值,以此类推        if (j == -1 || p[i] == p[j])        {            ++i;            ++j;            next[i] = j;        }        else        {            j = next[j];        }    }}
复制代码
0 0
原创粉丝点击