HDU 1711 Number Sequence (kmp)

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;
}

猜你喜欢

转载自blog.csdn.net/mengxianglong123/article/details/81812404