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
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
Source
HDU 2007-Spring Programming Contest
解题思路:kmp模板题 java内置的方法过不去 得手写kmp 题意是给两个串 匹配第二个串在第一个串中第一次出现的位置
package kmp;
import java.util.*;
public class hdu1711 {
public static int n;
public static int m;
public static int [] a;
public static int [] b;
public static int [] next;
/**
* hdu1711 kmp模板题
* 题意 给两个数组 找第二个数组在第一个数组第一次出现的位置
* java内置的方法A不过去这道题
*/
public static void main (String [] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
for(int i = 0;i < t; i ++){
n = sc.nextInt();
m = sc.nextInt();
a = new int [n];
b = new int [m];
for(int j = 0;j < n;j ++){
a[j] = sc.nextInt();
}
for(int j = 0;j < m;j++){
b[j] = sc.nextInt();
}
getNext();
int sum = kmp();
System.out.println(sum == -1?-1:sum-m+1);
}
}
//此方法利用已经弄好的next数组进行两个字符串的匹配
//也就是kmp算法
public static int kmp() {
int index_a = 0;//字符串a的下标
int index_b = 0;//字符串b的下标
while(index_a < n){
if(index_b == -1 || a[index_a] == b[index_b]){
//当 index_b = -1 时 说明匹配失败要从b字符串第一个字符重新开始匹配
//当 两个字符相等时 那不用想继续++ 匹配下一个就行了
index_a ++;
index_b ++;
}else{
//这条语句是用来告诉计算下一次匹配从b字符串的那个字符开始匹配
//这就用上了求的next数组里面的值
//为什么要用kmp算法呢 就在这句话上
//匹配的时候我不能每次匹配失败我都从b字符串的第一个位置开始匹配
//很浪费时间 所以就从上一个匹配好的地方开始匹配好了
//那么上一个匹配好的地方是哪里呢?
//答:在next数组里面 所以底下的语句就这么写
index_b =next[index_b];
}
if(index_b == m){
//当index_b == m 也就是所有字符都相等了
//那么此时index_a 就是字符串b在字符串a中第一次出现的末尾位置
return index_a;
}
}
return -1;
}
//此方法是获得next数组
public static void getNext(){
next = new int [n+1];
int index = 0; //这是初始的下标
int value = -1;//初始的跳转值 这个值必须是-1 稍后会解释为什么是-1
next[0] = -1; //
while(index < m){ //下标从0开始要小于字符串的总长度
if(value == -1 || b[index] == b[value]){
//当value=-1表示以index所对应的字符结尾的最大相同子串长度为0
//当b[index] = b[value] 表示以index所对应的字符结尾的最大相同子串长度在原先的基础上加1
index ++;//匹配下一个
value ++;//匹配下一个
next[index] = value; //赋上相应的跳转值
}else{
//当以上两个条件都不满足的时候
//要进行回退匹配
//为什么要回退呢?
//因为当匹配的两个字符不相等的时候
//此时index下标对应next值(也就是最大相同子串长度)不一定为0
//因为不相等 所以你不能直接在原基础上加1也不能直接赋成0
//那要做什么呢?
//所以接下来要进行回退匹配
//既然要回退那为什么写成下面这个样子呢?
//因为你要匹配最大相同子串 也就是光两个字符相等是不行的
//因为在这两个字符前面的串不一定相同
//那么在进行回退比较两个字符之前(也就是上面的if语句)
//你得保证这两个字符前面的串是一样的 不然你的比较没有意义啊
//那么我怎么才能保证这两个字符前面的串是一样的呢?
//还记得next存的是什么吗?
//next数组存的就是最大长度的值next数组存的就是最大长度的值
//这个值有多大那么相同的子串就有多长
//所以next[value] 也就是我们要回退 在那里进行匹配的位置
//在这里解释一下为什么初始值是-1
//在这个else与语句里我们要进行的回退操作
//那我不能一直回退啊 退到第一个字符就不能再退了
//所以 value的初始值和next【0】的值相同的
//那为什么赋-1呢?
//因为 字符一个一个匹配所以你进行的一定是++操作 初始值再进行++操作会变成0 你要写别的值他变不了0 next数组存的就不是你想要的值
value = next[value];
}
}
}
}
解题思路:kmp模板题 java内置的方法过不去 得手写kmp 题意是给两个串 匹配第二个串在第一个串中第一次出现的位置
package kmp;
import java.util.*;
public class hdu1711 {
public static int n;
public static int m;
public static int [] a;
public static int [] b;
public static int [] next;
/**
* hdu1711 kmp模板题
* 题意 给两个数组 找第二个数组在第一个数组第一次出现的位置
* java内置的方法A不过去这道题
*/
public static void main (String [] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
for(int i = 0;i < t; i ++){
n = sc.nextInt();
m = sc.nextInt();
a = new int [n];
b = new int [m];
for(int j = 0;j < n;j ++){
a[j] = sc.nextInt();
}
for(int j = 0;j < m;j++){
b[j] = sc.nextInt();
}
getNext();
int sum = kmp();
System.out.println(sum == -1?-1:sum-m+1);
}
}
//此方法利用已经弄好的next数组进行两个字符串的匹配
//也就是kmp算法
public static int kmp() {
int index_a = 0;//字符串a的下标
int index_b = 0;//字符串b的下标
while(index_a < n){
if(index_b == -1 || a[index_a] == b[index_b]){
//当 index_b = -1 时 说明匹配失败要从b字符串第一个字符重新开始匹配
//当 两个字符相等时 那不用想继续++ 匹配下一个就行了
index_a ++;
index_b ++;
}else{
//这条语句是用来告诉计算下一次匹配从b字符串的那个字符开始匹配
//这就用上了求的next数组里面的值
//为什么要用kmp算法呢 就在这句话上
//匹配的时候我不能每次匹配失败我都从b字符串的第一个位置开始匹配
//很浪费时间 所以就从上一个匹配好的地方开始匹配好了
//那么上一个匹配好的地方是哪里呢?
//答:在next数组里面 所以底下的语句就这么写
index_b =next[index_b];
}
if(index_b == m){
//当index_b == m 也就是所有字符都相等了
//那么此时index_a 就是字符串b在字符串a中第一次出现的末尾位置
return index_a;
}
}
return -1;
}
//此方法是获得next数组
public static void getNext(){
next = new int [n+1];
int index = 0; //这是初始的下标
int value = -1;//初始的跳转值 这个值必须是-1 稍后会解释为什么是-1
next[0] = -1; //
while(index < m){ //下标从0开始要小于字符串的总长度
if(value == -1 || b[index] == b[value]){
//当value=-1表示以index所对应的字符结尾的最大相同子串长度为0
//当b[index] = b[value] 表示以index所对应的字符结尾的最大相同子串长度在原先的基础上加1
index ++;//匹配下一个
value ++;//匹配下一个
next[index] = value; //赋上相应的跳转值
}else{
//当以上两个条件都不满足的时候
//要进行回退匹配
//为什么要回退呢?
//因为当匹配的两个字符不相等的时候
//此时index下标对应next值(也就是最大相同子串长度)不一定为0
//因为不相等 所以你不能直接在原基础上加1也不能直接赋成0
//那要做什么呢?
//所以接下来要进行回退匹配
//既然要回退那为什么写成下面这个样子呢?
//因为你要匹配最大相同子串 也就是光两个字符相等是不行的
//因为在这两个字符前面的串不一定相同
//那么在进行回退比较两个字符之前(也就是上面的if语句)
//你得保证这两个字符前面的串是一样的 不然你的比较没有意义啊
//那么我怎么才能保证这两个字符前面的串是一样的呢?
//还记得next存的是什么吗?
//next数组存的就是最大长度的值next数组存的就是最大长度的值
//这个值有多大那么相同的子串就有多长
//所以next[value] 也就是我们要回退 在那里进行匹配的位置
//在这里解释一下为什么初始值是-1
//在这个else与语句里我们要进行的回退操作
//那我不能一直回退啊 退到第一个字符就不能再退了
//所以 value的初始值和next【0】的值相同的
//那为什么赋-1呢?
//因为 字符一个一个匹配所以你进行的一定是++操作 初始值再进行++操作会变成0 你要写别的值他变不了0 next数组存的就不是你想要的值
value = next[value];
}
}
}
}