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
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
Sample Output
6 -1
kmp算法就是一个字符串匹配算法,它的精华部分就是最长相等前后缀,不过最不容易理解的就是在找最长相等前后缀的过程,其中最难理解的过程就是,当遇到前面的字符和后面的字符不相等的时候的操作,前面的那个指针要返回到它的上一个字符的nxt[]所对用的下标,下面来解释一下,(假设j在左,i在右)nxt数组存储的是字符串到达当前位置的时候最长相等前后缀的长度,因为我们是从0开始使用存储字符串的数组的,所以,这个nxt的数值大小刚好对应的是最长相等子序列的下一个元素,这样去回溯的话,效率会更高,比如当i=28时最长相等前后缀的长度是13(此时j=12),当i=29(j=13)时发现不相等了,这个时候其实我们需要往前找了(j减小),这里有一个更加高效的回溯方法,如果字符串长度为12时存在最长相等前后缀,那么我们就可以把j返回到前缀的的后一个字符,因为当i=19时,它只有最后一个字符鱼前面(也就是后缀的后一位)不匹配,所以它完全可以返回到前缀的后一个字符位置,这样只需要比较这个位置与i=19就可以了,这样的效率更高,如果这样能匹配成功,就不需要再返回首位再一个一个匹配,其他的问题那些大佬的博客都讲得很清楚,不再过多阐述。
#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
#define maxn 1000000+5
int a[maxn];
int b[maxn];
int nxt[maxn];
int n,m;
void m_nxt()
{
int i,j;
nxt[0]=0;
i=1;//右指针
j=0;//左指针
while(i<m)
{
if(b[i]==b[j])
{
nxt[i++]=++j;//相等长度加一
}
else if(!j)
{
i++;//防止死循环 ,因为首位的nxt必定为0
}
else
{
j=nxt[j-1];//不相等时的回溯
}
}
}
int kmp()
{
int i,j;
i=0;
j=0;
while(i<n && j<m)//匹配过程
{
if(a[i]==b[j])
{
i++;//相等统一后移
j++;
}
else if(!j)
{
i++;//防止死循环
}
else
{
j=nxt[j-1];//这个不再阐述,大佬博客讲的很清楚
}
}
if(j==m) return i-m+1;//这个数学关系可以自己推一下
else return -1;
}
int main()
{
int t;
int i,j;
scanf("%d",&t);
while(t--)
{
memset(nxt,0,sizeof(nxt));//初始化
scanf("%d %d",&n,&m);
for(i=0;i<n;i++)
scanf("%d",&a[i]);
for(i=0;i<m;i++)
scanf("%d",&b[i]);
m_nxt();
cout<<kmp()<<endl;
}
return 0;
}