2 13 5 1 2 1 2 3 1 2 3 1 3 2 1 2 1 2 3 1 3 13 5 1 2 1 2 3 1 2 3 1 3 2 1 2 1 2 3 2 1
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]; } } }