杭电1711之kmp算法

来源:互联网 发布:电脑usb直连网络 编辑:程序博客网 时间:2024/05/17 03:33

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711  

                                    Number Sequence

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 11435    Accepted Submission(s): 5212


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
213 51 2 1 2 3 1 2 3 1 3 2 1 21 2 3 1 313 51 2 1 2 3 1 2 3 1 3 2 1 21 2 3 2 1

Sample Output
6-1
   在说题目之前还是要表达一下对严蔚敏老师的尊敬,毕竟kmp也是看了严老师的视频才有了较为深入的理解。强烈推荐严老师的数据结构视频,深刻细致又不乏生动可爱,给严老师点好多好多赞!
////////////////////////分割线进入正题//////////////////////////////////////
这道题目还是比较好理解的,就是找一下在一个较长的a字符串里边有没有一个与较短b字符串相匹配的子串,如果有的话输出第一个比配点的位置。本身就是一种简单的字符串匹配问题,所以我们很容易就想到了第一种方法 :朴素算法。这种算法的思想就像他的名字一样简单朴素。。就是挨个拿着a字符串与b字符串进行比较,如果不相同的话,a字符串的指针移到下一个位置从头与b字符串开始比较,找到完全比配之后最后返回第一次匹配完成的位置。
贴一下简单算法的代码
//朴素算法int index (char *s,char *t,int pos)//pos:起始位置{int i = pos;int j = 1;while(i <= strlen(s) && j <= strlen(t)){if(s[i] == t[j]){i++;j++;}else{i = i-j+2;j = 1; }}if(j > strlen(t))return i-j+1;elsereturn 0;} 
当然我们很容易就会发现这种朴素算法的简单是建立在所要匹配的数据量小的前提下的,他的时间复杂度已经达到了两字符串长度乘积的大小,所以面对这题数据量这么大的前提下,这种方法还是直接pass好了。。
当然还有一种叫做首尾匹配的算法。他的方法如下
1:比较第一个字符;
2:比较第二个字符;
3:比较第二个到第n-2个字符;
   这种方法的代码只是先加了对首尾字符的判断,其余跟朴素算法基本一样所以代码就不写了,但是我们还是可以发现,这种算法虽然比之朴素算法有了很大的改进空间,但是如果倒霉一直遇到那种变态测试这个时间复杂度还是到了跟前者一样的大小。。所以。。
好了,我们的kmp算法登场了!
    kmp算法用一种求出较短字符串(后文统称为模式串)到其本身每个字符为止,前缀部分和后缀部分相等部分的长度(一定好好理解这句!!),的方法消除了对长字符串指针回溯的过程,借此将时间复杂度由二者乘积降到了二者的加和!
大家先看这题代码
#include<stdio.h>#define maxn 1000005int a[maxn],b[10005];int next[10005];void getnext(int s[],int l){int i = 0,j = -1;next[0] = -1;while(i  < l){if(j == -1 || s[i] == s[j]){i++;j++;if(s[i] != s[j])next[i] = j;elsenext[i] = next[j];//next[i] = j; }elsej = next[j];}}int kmp(int s1,int s2){int i = 0;int j = 0; while(i < s1 && j < s2){if(j == -1 || a[i] == b[j]){i++;j++;}elsej = next[j];}if(j == s2)return i-s2+1;return 0;}int main(void){int t;int n,m;scanf("%d",&t);while(t--){scanf("%d%d",&n,&m);int i;for(i = 0; i < n; i++)scanf("%d",&a[i]);for(i = 0; i < m; i++)scanf("%d",&b[i]);getnext(b,m);int k;k = kmp(n,m);if(k)printf("%d\n",k);elseprintf("-1\n");}return 0;}
kmp算法里的核心部分就在于求next函数了,当然这一部分也是最难理解的部分,所以下面我们就举个栗子详细的分析一下这个过程
我们定义一个模式串为ABRACADABRA
下面我们就开始算这个next函数的值了。
         首先next【0】 一定是0,然后指针往下走到B,显然也不存在与前追相等的后缀字符,所以next【1】为0,继续走到C,这时候C前面出现了与开始A字符相匹配的字符A,所以next【4】变为1,接着走到下一个A,前面的后缀AC显然与开始前缀AB不相匹配,所以next【5】变为0,.....,就按照这种方法,我们可以一次得到模式串的状态转移表为next【6】为1,next【7】为0,next【8】为1,next【9】为2,next【10】为3,next【11】为4。至此我们的next函数值就确定了,然后在kmp函数调用next函数的时候,如果出现模式串与原来串不相匹配的字符,那么模式串上该字符就直接用该点对应的next函数值代换,转而继续判断,,剩下的工作就水到渠成。。
          当然next函数里还是有一定优化方式的,就是加了对模式串中这一句if(s[i] != s[j])    next[i] = j;   else    next[i] = next[j];这句可以减少像aaaaabaab这种模式串的计算效率,
话虽这样说,,,按照有无这种优化提交两次杭电亲测时间都是484ms。。。所以这里一笔带过先。。
 留坑。
 
 
0 0
原创粉丝点击