【题目描述】

法国作家乔治·佩雷克(Georges Perec,1936-1982)曾经写过一本书,《敏感字母》(La disparition),全篇没有一个字母‘e’。他是乌力波小组(Oulipo Group)的一员。下面是他书中的一段话:

Tout avait Pair normal, mais tout s’affirmait faux. Tout avait Fair normal, d’abord, puis surgissait l’inhumain, l’affolant. Il aurait voulu savoir où s’articulait l’association qui l’unissait au roman : stir son tapis, assaillant à tout instant son imagination, l’intuition d’un tabou, la vision d’un mal obscur, d’un quoi vacant, d’un non-dit : la vision, l’avision d’un oubli commandant tout, où s’abolissait la raison : tout avait l’air normal mais…

佩雷克很可能在下面的比赛中得到高分(当然,也有可能是低分)。在这个比赛中,人们被要求针对一个主题写出甚至是意味深长的文章,并且让一个给定的“单词”出现次数尽量少。我们的任务是给评委会编写一个程序来数单词出现了几次,用以得出参赛者最终的排名。参赛者经常会写一长串废话,例如500000个连续的‘T’。并且他们不用空格。

因此我们想要尽快找到一个单词出现的频数,即一个给定的字符串在文章中出现了几次。更加正式地,给出字母表{'A','B','C',...,'Z'}和两个仅有字母表中字母组成的有限字符串:单词W和文章T,找到W在T中出现的次数。这里“出现”意味着W中所有的连续字符都必须对应T中的连续字符。T中出现的两个W可能会部分重叠。

【输入格式】

输入包含多组数据。

输入文件的第一行有一个整数,代表数据组数。接下来是这些数据,以如下格式给出:

第一行是单词W,一个由{'A','B','C',...,'Z'}中字母组成的字符串,保证1<=|W|<=10000(|W|代表字符串W的长度)

第二行是文章T,一个由{'A','B','C',...,'Z'}中字母组成的字符串,保证|W|<=|T|<=1000000。

【输出格式】

对每组数据输出一行一个整数,即W在T中出现的次数。

【样例输入】

3

BAPC

BAPC

AZA

AZAZAZA

VERDI

AVERDXIVYERDIAN

【样例输出】

1

3

0

——————————————————————————————————————————————————————————————

题意:查找子串在主串中出现的次数。

子串查找匹配:KMP.

KMP算法初看非常晦涩难懂,网上代码的思想都是一样的,但是写法上有一些细节的不同,也就使得它更难掌握。在这里我会介绍一些我的理解,不一定好,只是自己的学习记录。如果不懂可以看下面的博客:http://blog.csdn.net/u011564456/article/details/20862555?utm_source=tuicool&utm_medium=referral ,非常通俗易懂。

KMP算法的思想:

普通的子串匹配常用的方法为:子串与主串比较,如果过程中失败则后移一位。如:

主串:a b c a d a b c a b

子串:a b c a b

子串:   a b c a b (后移后位置)

这样时间复杂度为(n*m);

三位大神分析发现,我们不必一位一位的移动,因为后移一位勘定无法匹配。而是应该把子串直接移到3号位(从0开始),从主串4号位与子串1号位开始比较,这样就加快了速度。而移动到几号位和子串中比较失败前的前缀中的公共前后缀长度有关,如比较到子串中的4号位b时失败,则前缀为 a b c a ,这样它的公共前后缀为a,所以从a后面的字符开始比较就可以了,a不用比了。主串中前面的位置也不用比了。这样任务就成了如何快速的求公共前后缀的长度,即next[]。

next[]的含义:

前面说到next[]为公共前后缀的长度,但是有一点点不同,应该这样理解:公共前后缀的前缀最后一个字符的序号。

如:next[i]表示子串前i+1个字符组成的前缀(0——i)的公共前后缀的前缀的最后一个字符的序号。如a b c a b 的next[]=-1、-1、-1、0、1。

next[]的求法:

next[0]=-1;

求f[i]时,如果zichuan[i]==zichuan[next[i-1]+1],即子串0——i-1组成的前缀的公共前后缀的前缀后的一个字符与后缀后的一个字符相同,则next[后缀后的一个字符的序号]=next[后缀的最后一个字符的序号]+1。

如上图子串中:已知next[i-1]为第一块绿色部分,而第二块绿色部分与之相同。如果第一块红色部分与第二块红色部分也相同则next[i]=next[i-1]+1;

如果zichuan[i]!=zichuan[next[i-1]+1],则next[i]肯定比next[i-1]小,为从“zichuan[0]开始的”一个与“从zichuan[i]往前“等长的串。而由于next[i-1]的存在,”从zichuan[i-1]往前“的串与”zichuan[next[i]]往前“等长的串相同。所以next[i]=next[next[i-1]]+1;当然,还有可能递归下去。

如上图子串中,next[i-1]为第一块绿色部分,第二块绿色部分与之相同。但是两块红色部分不相同,即zichuan[next[i-1]+1]!=zichuan[i]。所以next[i]的后缀只能是第二块红色部分和前面绿色块的一部分,假设是它前面加黑的的部分。由于next[i-1]的存在,第一块红色部分前面肯定也有有一块加黑的的部分。因为next[i]的存在,子串的开始部分肯定也有一段加黑点的部分,而且一个与zichuan[i]相同的字符(加红点的)(如果没有相同的字符,则需要继续递归!)。因此可以看出前两个加黑点的部分是相同的,zichuan[i]与加红点字符是相同的。也就是说next[i]==next[next[i-1]]+1。

样例代码:

  1. void getnext()//next[]数组
  2. {
  3. next[0]=-1;
  4. for(int i=1,j;i<l;++i)
  5. {
  6. j=next[i-1];
  7. while(s[i]!=s[j+1] && j>=0)j=next[j];
  8. next[i]=s[i]==s[j+1]?next[j]+1:-1;
  9. }
  10. }
  11. int KMPf()//子串出现的位置
  12. {
  13. int i=0,j=0;
  14. while(i<l&&j<ll)
  15. {
  16. if(s[i]==ss[j])
  17. {
  18. i++;j++;
  19. }
  20. else
  21. if(i==0)j++;
  22. else i=next[i-1]+1;
  23. }
  24. return i==l?j-l:-1;
  25. }
  26. int KMP()//统计子串的次数
  27. {
  28. int i=0,j=0;
  29. while(i<l&&j<ll)
  30. {
  31. if(s[i]==ss[j])
  32. {
  33. i++;j++;
  34. }
  35. else
  36. if(i==0)j++;
  37. else i=next[i-1]+1;
  38. if(i==l)
  39. {
  40. ans++;i=next[i-1]+1;
  41. }
  42. }
  43. return ans;
  44. }

——————————————————————————————————————————————————————————————

  1. 1 #include<cstdio>
  2. 2 #include<cstring>
  3. 3 #include<iostream>
  4. 4 #include<algorithm>
  5. 5
  6. 6 using namespace std;
  7. 7 char s[10005],ss[1000005];
  8. 8 int l,ll,n,ans;
  9. 9 int next[10005];
  10. 10 void init()
  11. 11 {
  12. 12 scanf("%s%s",s,ss);
  13. 13 l=strlen(s);ll=strlen(ss);
  14. 14 ans=0;
  15. 15 }
  16. 16 void getnext()
  17. 17 {
  18. 18 next[0]=-1;
  19. 19 for(int i=1,j;i<l;++i)
  20. 20 {
  21. 21 j=next[i-1];
  22. 22 while(s[i]!=s[j+1] && j>=0)j=next[j];
  23. 23 next[i]=s[i]==s[j+1]?j+1:-1;
  24. 24 }
  25. 25 }
  26. 26 int KMP()
  27. 27 {
  28. 28 int i=0,j=0;
  29. 29 while(i<l&&j<ll)
  30. 30 {
  31. 31 if(s[i]==ss[j])
  32. 32 {
  33. 33 i++;j++;
  34. 34 }
  35. 35 else
  36. 36 if(i==0)j++;
  37. 37 else i=next[i-1]+1;
  38. 38 if(i==l)
  39. 39 {
  40. 40 ans++;i=next[i-1]+1;
  41. 41 }
  42. 42 }
  43. 43 return ans;
  44. 44 }
  45. 45 int main()
  46. 46 {
  47. 47 freopen("oulipo.in","r",stdin);
  48. 48 freopen("oulipo.out","w",stdout);
  49. 49 scanf("%d",&n);
  50. 50 while(n--)
  51. 51 {
  52. 52 init();
  53. 53 getnext();
  54. 54 printf("%d\n",KMP());
  55. 55 }
  56. 56 fclose(stdin);
  57. 57 fclose(stdout);
  58. 58 return 0;
  59. 59 }

POJ 3461__KMP算法的更多相关文章

  1. poj 1523Tarjan算法的含义——求取割点可以分出的连通分量的个数

    poj 1523Tarjan算法的含义——求取割点可以分出的连通分量的个数 题目大意:如题目所示 给你一些关系图——连通图,想要问你有没有个节点,损坏后,可以生成几个互相独立的网络(也就是连通分量), ...

  2. POJ题目算法分类总结博客地址

    http://blog.csdn.net/sunbaigui/article/details/4421705 又从这个地址找了一些:http://blog.csdn.net/koudaidai/art ...

  3. STL(pair map set vector priority_queue) poj 3297

    POJ 3297 算法竞赛初级杂烩包 题意:学生选课,没个学生只能选一门课.大写字符是课的名字,小写是人名.如果课程后面有多个相同名字算一个,如果一个人选多门课,则他选不上课,输出课和每门课选课人数 ...

  4. 浅谈KMP算法及其next[]数组

    KMP算法是众多优秀的模式串匹配算法中较早诞生的一个,也是相对最为人所知的一个. 算法实现简单,运行效率高,时间复杂度为O(n+m)(n和m分别为目标串和模式串的长度) 当字符串长度和字符集大小的比值 ...

  5. [ACM训练] 算法初级 之 搜索算法 之 深度优先算法DFS (POJ 2251+2488+3083+3009+1321)

    对于深度优先算法,第一个直观的想法是只要是要求输出最短情况的详细步骤的题目基本上都要使用深度优先来解决.比较常见的题目类型比如寻路等,可以结合相关的经典算法进行分析. 常用步骤: 第一道题目:Dung ...

  6. [ACM训练] 算法初级 之 搜索算法 之 广度优先算法BFS (POJ 3278+1426+3126+3087+3414)

    BFS算法与树的层次遍历很像,具有明显的层次性,一般都是使用队列来实现的!!! 常用步骤: 1.设置访问标记int visited[N],要覆盖所有的可能访问数据个数,这里设置成int而不是bool, ...

  7. [ACM训练] 算法初级 之 基本算法 之 枚举(POJ 1753+2965)

    先列出题目: 1.POJ 1753 POJ 1753  Flip Game:http://poj.org/problem?id=1753 Sample Input bwwb bbwb bwwb bww ...

  8. 算法手记 之 数据结构(线段树详解)(POJ 3468)

    依然延续第一篇读书笔记,这一篇是基于<ACM/ICPC 算法训练教程>上关于线段树的讲解的总结和修改(这本书在线段树这里Error非常多),但是总体来说这本书关于具体算法的讲解和案例都是不 ...

  9. 算法手记 之 数据结构(堆)(POJ 2051)

    一篇读书笔记 书籍简评:<ACM/ICPC 算法训练教程>这本书是余立功主编的,代码来自南京理工大学ACM集训队代码库,所以小编看过之后发现确实很实用,适合集训的时候刷题啊~~,当时是听了 ...

随机推荐

  1. 前端网页打印插件print.js(可导出pdf)

    在前端开发中,想打印当前网页的指定区域内容,或将网页导出为多页的PDF,可以借助print.js实现,该插件轻量.简单.手动引入.不依赖其他库.示范项目github:https://github.co ...

  2. San Francisco Crime Classification非数值性多分类问题

    给出了旧金山以往犯罪活动的相关信息,预测特定条件下的犯罪情况 分析数据集和测试集信息: 训练集给出的信息有: 1.Dates:时间日期和时间,这里考虑时间对犯罪活动有影响,日期和下边的周几有相似处,取 ...

  3. eclips如何安装jetty插件

    转载自http://www.cnblogs.com/nightswatch/p/4639687.html的博文 eclipse中安装jetty插件并使用   一.eclipse中jetty插件安装: ...

  4. 聊一聊这个总下载量3603w的xss库,是如何工作的?

    上篇文章这一次,彻底理解XSS攻击讲解了XSS攻击的类型和预防方式,本篇文章我们来看这个36039K的XSS-NPM库(你没有看错就是3603W次, 36039K次,36,039,651次,数据来自h ...

  5. sql操作数据库(1)-->DDL、DML、DQL

    SQL 操作数据库 概念:结构化查询语言 Structured Quary Language 作用:  1.是一种数据库的查询的标准,对所有的数据库都支持  2.不同的数据库SQL语句可能有点不同 ( ...

  6. Pytest测试框架(五):pytest + allure生成测试报告

    Allure 是一款轻量级.支持多语言的开源自动化测试报告生成框架,由Java语言开发,可以集成到 Jenkins. pytest 测试框架支持Allure 报告生成. pytest也可以生成juni ...

  7. 风炫安全WEB安全学习第十九节课 XSS的漏洞基础知识和原理讲解

    风炫安全WEB安全学习第十九节课 XSS的漏洞基础知识和原理讲解 跨站脚本攻击(Cross-site scripting,通常简称为XSS) 反射型XSS原理与演示 交互的数据不会存储在数据库里,一次 ...

  8. CQRS与Event Sourcing之浅见

    引言 DDD是近年软件设计的热门.CQRS与Event Sourcing作为实施DDD的一种选择,也逐步进入人们的视野.围绕这两个主题,软件开发的大咖[Martin Fowler].[Greg You ...

  9. WebSocket协议中文版

    WebSocket协议中文版 摘要 WebSocket协议实现在受控环境中运行不受信任代码的一个客户端到一个从该代码已经选择加入通信的远程主机之间的全双工通信.用于这个安全模型是通常由web浏览器使用 ...

  10. Go GRPC 入门(一)

    前言 微服务相关 使用 GRPC 通讯的 Golang 微服务入门 举例写一个微服务,接收网址发送请求获取返回结果返回 正文 安装工具 安装 protobuf 这是 proto 文件的编译器 点我下载 ...