题目代号:HDU 1711

题目链接: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): 28288    Accepted Submission(s): 11891

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
 

题目大意:给你两个数组的所有元素,让你对它们进行匹配,当位置为多少时候它们能完全匹配。

解题思路:

  现在我们对第一个例子的数组进行分析,我们一开始想到的肯定是通过循环一个一个去进行匹配,碰见不匹配了就退出一层循环,然后重新搜索进行匹配,如下所示:

至此,匹配已经全部完成,代码也非常简单

  1. # include <iostream>
  2. # include <cstring>
  3. # include <cstdlib>
  4. # include <cstdio>
  5. # include <cmath>
  6. # include <ctime>
  7. # include <set>
  8. # include <map>
  9. # include <queue>
  10. # include <stack>
  11. # include <vector>
  12. # include <fstream>
  13. # include <algorithm>
  14. using namespace std;
  15. # define eps 1e-
  16. # define pb push_back
  17. # define pi acos(-1.0)
  18. # define bug puts("H")
  19. # define mem(a,b) memset(a,b,sizeof(a))
  20. # define IOS ios::sync_with_stdio(false)
  21. # define FO(i,n,a) for(int i=n; i>=a; --i)
  22. # define FOR(i,a,n) for(int i=a; i<=n; ++i)
  23. # define INF 0x3f3f3f3f
  24. # define MOD
  25. /// 123456789
  26. # pragma comment(linker, "/STACK:1024000000,1024000000")
  27. typedef unsigned long long ULL;
  28. typedef long long LL;
  29. inline int Scan() {
  30. int x=,f=; char ch=getchar();
  31. while(ch<''||ch>''){if(ch=='-') f=-; ch=getchar();}
  32. while(ch>=''&&ch<=''){x=x*+ch-''; ch=getchar();}
  33. return x*f;
  34. }
  35. ///coding...................................
  36.  
  37. int a[],b[];
  38.  
  39. int main()
  40. {
  41. IOS;
  42. #ifdef FLAG
  43. freopen("in.txt","r",stdin);
  44. //freopen("out.txt","w",stdout);
  45. #endif /// FLAG
  46. int t;
  47. cin>>t;
  48. int n,m;
  49. while(t--) {
  50. cin>>n>>m;
  51. int flag=;
  52. FOR(i,,n)cin>>a[i];
  53. FOR(i,,m)cin>>b[i];
  54. FOR(i,,n-m+) {
  55. FOR(j,,m) {
  56. if(b[j]!=a[i+j-])break;
  57. if(j==m&&b[j]==a[i+j-])flag=i;
  58. }
  59. if(flag)break;
  60. }
  61. if(flag)cout<<flag<<endl;
  62. else cout<<-<<endl;
  63. }
  64. return ;
  65. }

可是还有一个问题呢!提交之后显示TEL,超时!!!为什么呢,这是显然的,这是两重循环一个一个的匹配完成的在最坏的情况下时间复杂度接近O(nm)那么很显然,m最大的值可达10^6而n可达10^4,那么综合时间复杂度接近10^10,而oj上一秒钟只能跑10^7~10^8,那么我们应该怎么去解决这一个问题呢?通过上面的一步一步的观察,我们可以发现其中是有着很多重复的步骤,那我们要怎样才能将这个重复的步骤次数减少到可以接受的情况呢?这就是我下面要介绍的一个算法——KMP,KMP是扩展KMP以及AC自动机的基础所以作为一个ACMER我们一定要学会这个贼强的算法,而不是通过套KMP的模板来完成题目。好了下面切入正题,KMP中最重要的东西就是一个预处理的过程,通过定义一个next数组来储存之后应该所移动的位置量。

下面是next数组的预处理核心代码:

  1. void find_next_() {
  2. int j=,i=;
  3. next_[]=;
  4. while(i<m) {
  5. if(j==||b[j]==b[i]) {
  6. ++i,++j;
  7. if(b[i]!=b[j])next_[i]=j;
  8. else next_[i]=next_[j];
  9. }
  10. else j = next_[j];
  11. }
  12. }

这回是以下图为例来进行示范:(因为上面的例子不太好用)

我们使用KMP预处理对B数组进行一个搜索,然后得到一个next数组,实际上就是统计出B中相同的连续元素,然后进行一个标记的操作

然后我们经过处理后得到的next数组为:我们先不要管这个数组为什么要通过计算,以及这个数组的作用是什么,我们再来模拟一遍KMP的操作,因为时间原因,我就简单操作一遍,我们直接跳到不同的那个步骤:

经过我们观察,是不是能够发现B[6]之前有着相同的元素1,2,1,那么我们是不是能够让相同的部分重叠,来减少这个匹配所移动的次数呢?

你看这样是不是能一步达到我们所需要的条件,然后我们继续操作,

你看,我们我们又节省了一部分时间,但是没有1 2 1可以匹配了怎么办?没关系,接着往下看,

这样我们又将两个1匹配在了一起,但是A[i]!=B[j],并且之前只有一个元素可以匹配,完全不能移动了,那么我们应该怎么继续操作呢?什么,B[ ]整体向右移动?没错!这样我们又能得到,

哇,所有匹配都全部完成了诶!你看,这样是不是很能节省我们之前那种操作循环所需要的时间?其实KMP当中的预处理就是扮演了一个这样的角色,他告诉我们当A[i]!=B[j]时我们应该怎样整体移动B[ ]的数组,next预处理函数所做的只是查找当A[i]与B[j]不匹配时,A[i]之前的后m个元素与B[ ]数组开头的前m个元素顺序相同时的提取下标的操作,你看看之前的操作是不是这样进行操作的呢?

好了,KMP的核心预处理的讲解就告一段落了,相信如果认真的去看之前的操作,都能够清晰了解KMP的具体操作了,下面放上完整的AC代码:

  1. # include <iostream>
  2. # include <cstring>
  3. # include <cstdlib>
  4. # include <cstdio>
  5. # include <cmath>
  6. # include <ctime>
  7. # include <set>
  8. # include <map>
  9. # include <queue>
  10. # include <stack>
  11. # include <vector>
  12. # include <fstream>
  13. # include <algorithm>
  14. using namespace std;
  15. # define eps 1e-
  16. # define pb push_back
  17. # define pi acos(-1.0)
  18. # define bug puts("H")
  19. # define mem(a,b) memset(a,b,sizeof(a))
  20. # define IOS ios::sync_with_stdio(false)
  21. # define FO(i,n,a) for(int i=n; i>=a; --i)
  22. # define FOR(i,a,n) for(int i=a; i<=n; ++i)
  23. # define INF 0x3f3f3f3f
  24. # define MOD
  25. /// 123456789
  26. # pragma comment(linker, "/STACK:1024000000,1024000000")
  27. typedef unsigned long long ULL;
  28. typedef long long LL;
  29. inline int Scan(){
  30. int x=,f=;char ch=getchar();
  31. while(ch<''||ch>''){if(ch=='-') f=-;ch=getchar();}
  32. while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
  33. return x*f;
  34. }
  35. ///coding...................................
  36.  
  37. int a[];
  38. int b[];
  39. int next_[];
  40. int m,n;
  41.  
  42. void find_next_() {
  43. int j=,i=;
  44. next_[]=;
  45. while(i<m) {
  46. if(j==||b[j]==b[i]) {
  47. ++i,++j;
  48. if(b[i]!=b[j])next_[i]=j;
  49. else next_[i]=next_[j];
  50. }
  51. else j = next_[j];
  52. }
  53. }
  54. int kmp() {
  55. int i=,j=;
  56. while(i<=n&&j<=m) {
  57. if(j==||a[i]==b[j])++i,++j;
  58. else j=next_[j];
  59. }
  60. if(j==m+)return i-m;
  61. else return -;
  62. }
  63. int main()
  64. {
  65. IOS;
  66. #ifdef FLAG
  67. freopen("in.txt","r",stdin);
  68. //freopen("out.txt","w",stdout);
  69. #endif /// FLAG
  70. int t;
  71. cin>>t;
  72. while(t--)
  73. {
  74. cin>>n>>m;
  75. FOR(i,,n)cin>>a[i];
  76. FOR(i,,m)cin>>b[i];
  77. find_next_();
  78. cout<<kmp()<<endl;
  79. }
  80. return ;
  81. }
作者:韵祈

    

    

本博客中未标明转载的文章归作者韵祈和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

HDU 1711 Number Sequence(KMP)附带KMP的详解的更多相关文章

  1. HDU 1711 Number Sequence (字符串匹配,KMP算法)

    HDU 1711 Number Sequence (字符串匹配,KMP算法) Description Given two sequences of numbers : a1, a2, ...... , ...

  2. HDU 1711 Number Sequence(数列)

    HDU 1711 Number Sequence(数列) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Ja ...

  3. HDU 1711 Number Sequence 【KMP应用 求成功匹配子串的最小下标】

    传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1711 Number Sequence Time Limit: 10000/5000 MS (Java/O ...

  4. HDU 1711 Number Sequence(KMP裸题,板子题,有坑点)

    Number Sequence Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  5. HDU 1711 Number Sequence (KMP简单题)

    Number Sequence Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  6. hdu 1711 Number Sequence KMP 基础题

    Number Sequence Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  7. KMP - HDU 1711 Number Sequence

    Number Sequence Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  8. HDU 1711 Number Sequence(kmp)

    Problem Description Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], .... ...

  9. HDU 1711 Number Sequence(KMP模板)

    http://acm.hdu.edu.cn/showproblem.php?pid=1711 这道题就是一个KMP模板. #include<iostream> #include<cs ...

随机推荐

  1. 移除list里面的值

    public class IteratorTest { public static void main(String[] args) { List<String> list = new A ...

  2. kafka 教程(三)-远程访问

    远程连接 kafka 配置 默认的 kafka 配置是无法远程访问的,解决该问题有几个方案. 方案1 advertised.listeners=PLAINTEXT://IP:9092 注意必须是 ip ...

  3. unittest中的testCase执行顺序

    1.方法顺序 def setUp(self): 在测试方法前执行 def tearDown(self): 在测试方法后执行 class TestMethod(unittest.TestCase): # ...

  4. Almost Increasing Array CodeForces - 946G (dp)

    大意: 定义几乎递增序列为删除不超过一个数后序列严格递增. 给定序列, 求最少改变多少个数能变为几乎递增序列. 跟hdu5256类似,

  5. 掌握 analyze API,一举搞定 Elasticsearch 分词难题

    初次接触 Elasticsearch 的同学经常会遇到分词相关的难题,比如如下这些场景: 为什么明明有包含搜索关键词的文档,但结果里面就没有相关文档呢? 我存进去的文档到底被分成哪些词(term)了? ...

  6. C++11随机数的正确打开方式

    C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...

  7. 玩转Android状态栏

    前言 前段时间,突然收到一个状态栏颜色优化设计的任务,将原本应用整体的黑色状态栏修改为根据标题栏颜色进行沉浸式设计,显示效果如下:   image 经过分析及踩过N多坑,终于完成了APP全局的修改.现 ...

  8. Linux练习例题(附答案)

    1.通过ps命令的两种选项形式查看进程信息 2.通过top命令查看进程 3.通过pgrep命令查看sshd服务的进程号 4.查看系统进程树 5.使dd if=/dev/zero of=/root/fi ...

  9. AIX中的/etc/inittab文件

    1./etc/inittab文件   /etc/inittab文件从上到下逐行表述了某个服务或应用的启动需求.运行级别.应用脚本,格式如下: identifier:Runlevel:  Action: ...

  10. super、this关键字

    super调用(父类) 调用方法   语法: super.父类方法名(形参列表);        可以在子类方法和构造器中使用,调用父类被覆盖的方法. 实例变量 语法: super.父类实例变量名;  ...