1. 程式人生 > >HDU 1711 Number Sequence (kmp)

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