kmp板子如下, 失配数组不优化的话, $f_i$就表示子串[0...i]前后缀最大匹配长度

  1. int main() {
  2. scanf("%s%s", t, p);
  3. int n = strlen(t), m = strlen(p);
  4. f[0]=f[1]=0;
  5. int j = 0;
  6. REP(i,1,m-1) {
  7. while (j&&p[i]!=p[j]) j=f[j];
  8. if (p[i]==p[j]) ++j;
  9. f[i+1] = j;
  10. }
  11. j = 0;
  12. REP(i,0,n-1) {
  13. while (j&&p[j]!=t[i]) j=f[j];
  14. if (p[j]==t[i]) ++j;
  15. if (j==m) printf("%d", i-m+1);
  16. }
  17. }

练习1: hdu5763

大意: 给定字符串T, 模板串P, 可以将T中与P匹配的子串替换为'*', 求多少种替换方案.

一个板子题, kmp求出可以替换的位置, 然后dp就好了

  1. const int N = 1e6+10;
  2. char t[N], p[N];
  3. int f[N], ff[N], *dp=ff+1;
  4. void add(int &a, int b) {a+=b;if (a>=P) a-=P;}
  5.  
  6. void work() {
  7. scanf("%s%s", t, p);
  8. int n = strlen(t), m = strlen(p);
  9. f[0]=f[1]=0;
  10. REP(i,1,m-1) {
  11. int j = f[i];
  12. while (j&&p[i]!=p[j]) j=f[j];
  13. if (p[i]==p[j]) ++j;
  14. f[i+1] = j;
  15. }
  16. int j = 0;
  17. dp[-1] = 1;
  18. REP(i,0,n-1) {
  19. while (j&&p[j]!=t[i]) j=f[j];
  20. if (p[j]==t[i]) ++j;
  21. dp[i] = 0;
  22. if (j==m) add(dp[i],dp[i-m]);
  23. add(dp[i],dp[i-1]);
  24. }
  25. printf("%d\n",dp[n-1]);
  26. }
  27.  
  28. int main() {
  29. int t;
  30. scanf("%d", &t);
  31. REP(i,1,t) printf("Case #%d: ",i),work();
  32. }

练习2 CF825F

大意: 给定字符串$s$, 可以将连续$c1$个相同的子串$s1$压缩为|c1|+|s1|, 求压缩若干次后$s$最短长度.

要用到一个字符串循环节的结论, 假设一个长$n$的字符串$s$, 失配函数为$f$, 则循环节为n-f[n]或n

  1. const int N = 8e3+10;
  2. int n;
  3. char s[N];
  4. int f[N], g[N][N], ff[N], *dp=ff+1;
  5. void getFail(char *s) {
  6. int m = strlen(s);
  7. f[0]=f[1]=0;
  8. REP(i,1,m-1) {
  9. int j=f[i];
  10. while (j&&s[i]!=s[j]) j=f[j];
  11. if (s[i]==s[j]) ++j;
  12. f[i+1] = j;
  13. }
  14. }
  15. int calc(int x) {int r=0;while (x) ++r,x/=10;return r;}
  16.  
  17. int main() {
  18. scanf("%s", s);
  19. n = strlen(s);
  20. REP(i,0,n-1) {
  21. getFail(s+i);
  22. REP(j,i,n-1) {
  23. int len = j-i+1;
  24. if (len%(len-f[len])==0) g[i][j]=len-f[len]+calc(len/(len-f[len]));
  25. else g[i][j]=len+1;
  26. }
  27. }
  28. dp[-1] = 0;
  29. REP(i,0,n-1) {
  30. dp[i] = INF;
  31. REP(j,0,i) dp[i] = min(dp[i], dp[j-1]+g[j][i]);
  32. }
  33. printf("%d\n", dp[n-1]);
  34. }

练习3 CF494B

这题想了好长时间, 计数还是不熟练啊

$dp[i]=j+1+\sum\limits_{k\le j-1} sum[k]$

dp[i]是以i为右端点的方案, sum[i]是dp[i]的前缀和, j为上次匹配位置

  1. int n, m;
  2. char t[N], p[N];
  3. int dp[N], s1[N], s2[N], f[N], g[N];
  4. void add(int &a, int b) {a+=b;if (a>=P)a-=P;}
  5.  
  6. int main() {
  7. scanf("%s%s", t, p);
  8. n = strlen(t), m = strlen(p);
  9. f[0]=f[1]=0;
  10. REP(i,1,m-1) {
  11. int j=f[i];
  12. while (j&&p[j]!=p[i]) j=f[j];
  13. if (p[j]==p[i]) ++j;
  14. f[i+1]=j;
  15. }
  16. int j=0;
  17. REP(i,0,n-1) {
  18. while (j&&t[i]!=p[j]) j=f[j];
  19. if (t[i]==p[j]) ++j;
  20. if (j==m) g[i]=1;
  21. }
  22. REP(i,0,n-1) {
  23. if (g[i]) j=i-m+1,dp[i]=s2[j-1],add(dp[i],j+1);
  24. else if (i) dp[i] = dp[i-1];
  25. if (i) s1[i] = s1[i-1];
  26. add(s1[i],dp[i]);
  27. if (i) s2[i] = s2[i-1];
  28. add(s2[i],s1[i]);
  29. }
  30. printf("%d\n",s1[n-1]);
  31. }

练习4 CF 1015 Bracket Substring

大意:求长度为2n, 给定括号串$s$, 求所有以$s$为子串的合法括号序列种类数.

kmp经典套路了, 设$dp[i][j][k]$表示到$i$位时, 已经匹配$j$位, 左右括号差为$k$的方案数

对于放'(', 若s[j]为'('则匹配位数转移到j+1, 否则沿失配边走. 对于')'的情况同理.

要注意特殊处理已经转移完毕的情况. 这里可以将失配函数优化一下, 复杂度是$O(\omega^2 n^2m), \omega$为字符种类数, 不优化的话是$O(\omega n^2m^2)$实际上也能跑过.

  1. const int N = 210;
  2. int n, m;
  3. char s[N];
  4. int dp[N][N][N], f[N];
  5.  
  6. void add(int &a, int b) {a+=b;if (a>=P)a-=P;}
  7.  
  8. int main() {
  9. scanf("%d%s", &n, s);
  10. m = strlen(s);
  11. REP(i,1,m-1) {
  12. int j = f[i];
  13. while (j&&s[i]!=s[j]) j=f[j];
  14. if (s[i]==s[j]) ++j;
  15. f[i+1]=j;
  16. }
  17. REP(i,1,m-1) if (s[i]==s[f[i]]) f[i]=f[f[i]];
  18. dp[0][0][0]=1;
  19. REP(i,1,2*n) REP(j,0,m) REP(k,0,n) if (dp[i-1][j][k]) {
  20. int nxt = j;
  21. while (nxt&&s[nxt]!='(') nxt=f[nxt];
  22. if (s[nxt]=='(') ++nxt;
  23. if (j==m) nxt=m;
  24. add(dp[i][nxt][k+1],dp[i-1][j][k]);
  25. if (!k) continue;
  26. nxt = j;
  27. while (nxt&&s[nxt]!=')') nxt=f[nxt];
  28. if (s[nxt]==')') ++nxt;
  29. if (j==m) nxt=m;
  30. add(dp[i][nxt][k-1],dp[i-1][j][k]);
  31. }
  32. printf("%d\n", dp[2*n][m][0]);
  33. }

练习5 HDU3689 Infinite monkey theorem

跟上题类似, 借助kmp进行转移

  1. const int N = 1e3+10;
  2. int n, m;
  3. double q[N], dp[N][22];
  4. char s[N];
  5. int f[N];
  6.  
  7. void work() {
  8. REP(i,'a','z') q[i]=0;
  9. REP(i,1,n) {
  10. char c;
  11. double x;
  12. scanf(" %c%lf", &c, &x);
  13. q[c] = x;
  14. }
  15. scanf("%s", s);
  16. n = strlen(s);
  17. REP(i,1,n-1) {
  18. int j=f[i];
  19. while (j&&s[j]!=s[i]) j=f[j];
  20. if (s[j]==s[i]) ++j;
  21. f[i+1]=j;
  22. }
  23. REP(i,1,n-1) if (s[i]==s[f[i]]) f[i]=f[f[i]];
  24. memset(dp,0,sizeof dp);
  25. dp[0][0] = 1;
  26. REP(i,1,m) REP(j,0,n) {
  27. REP(k,'a','z') {
  28. int nxt = j;
  29. while (nxt&&s[nxt]!=k) nxt=f[nxt];
  30. if (s[nxt]==k) ++nxt;
  31. if (j==n) nxt=n;
  32. dp[i][nxt] += q[k]*dp[i-1][j];
  33. }
  34. }
  35. printf("%.2lf%%\n", 100*dp[m][n]);
  36. }
  37.  
  38. int main() {
  39. for (; scanf("%d%d", &n, &m), n||m; ) work();
  40. }

练习6 CF 432D Prefixes and Suffixes

大意: 给定字符串$s$, 求$s$有多少个前缀等于后缀, 并且输出它们的出现次数

kmp经典题了, 失配函数如果不优化的话, 那么$f_i$就表示子串[0,i-1]中最大的使前缀等于后缀的前缀长度.

也就是说$s$满足要求的最大前缀长度是$f[n]$, 其余前缀一定在$f[n]$内, 这样就可以迭代求出满足要求的前缀位置.

再用$dp$求出每个前缀的出现次数即可.

  1. const int N = 1e6+10;
  2. int n;
  3. char s[N];
  4. int f[N], dp[N], a[N];
  5.  
  6. int main() {
  7. scanf("%s", s);
  8. n = strlen(s);
  9. REP(i,1,n-1) {
  10. int j = f[i];
  11. while (j&&s[i]!=s[j]) j=f[j];
  12. if (s[i]==s[j]) ++j;
  13. f[i+1]=j;
  14. }
  15. for (int nxt=n; nxt; nxt=f[nxt]) a[++*a]=nxt;
  16. PER(i,1,n) dp[f[i]]+=++dp[i];
  17. printf("%d\n", *a);
  18. PER(i,1,*a) printf("%d %d\n", a[i], dp[a[i]]);
  19. }

练习7 CF808G Anthem of Berland

先预处理了一下第i位是否能匹配到第j位, 最后再dp一遍

  1. const int N = 1e6+10;
  2. int n, m;
  3. char s[N], t[N];
  4. vector<vector<int> > g;
  5. int dp[N], mx[N], f[N], ff[N];
  6.  
  7. int main() {
  8. scanf("%s%s", s+1, t);
  9. n = strlen(s+1), m = strlen(t);
  10. if (n<m) return puts("0"),0;
  11. REP(i,0,n+10) g.pb(vector<int>());
  12. REP(i,0,n+10) REP(j,0,m+10) g[i].pb(0);
  13. REP(i,1,m-1) {
  14. int j = f[i];
  15. while (j&&t[j]!=t[i]) j=f[j];
  16. if (t[j]==t[i]) ++j;
  17. f[i+1]=j;
  18. }
  19. memcpy(ff,f,sizeof ff);
  20. REP(i,1,m-1) if (t[i]==t[f[i]]) f[i]=f[f[i]];
  21. REP(i,0,n) g[i][0] = 1;
  22. REP(i,1,n) REP(j,0,m) if (g[i-1][j]) {
  23. int nxt = j;
  24. while (nxt&&t[nxt]!=s[i]) nxt=f[nxt];
  25. if (t[nxt]==s[i]) ++nxt;
  26. g[i][nxt] = 1;
  27. if (s[i]=='?') g[i][j+1] = 1;
  28. }
  29. REP(i,1,n) {
  30. if (g[i][m]) {
  31. dp[i] = mx[i-m]+1;
  32. for (int j=ff[m]; j; j=ff[j]) {
  33. dp[i] = max(dp[i], dp[i-m+j]+1);
  34. }
  35. }
  36. mx[i] = max(dp[i], mx[i-1]);
  37. }
  38. printf("%d\n", mx[n]);
  39. }

看了下别人题解后发现自己想复杂了....只需要判断是否能匹配到$m$位, 暴力判断就好了, 并且复杂度会少一个$\omega$,但实际跑了下没差多少.

  1. const int N = 1e6+10;
  2. int n, m;
  3. char s[N], t[N];
  4. int dp[N], mx[N], f[N];
  5.  
  6. int main() {
  7. scanf("%s%s", s+1, t);
  8. n = strlen(s+1), m = strlen(t);
  9. if (n<m) return puts("0"),0;
  10. REP(i,1,m-1) {
  11. int j = f[i];
  12. while (j&&t[j]!=t[i]) j=f[j];
  13. if (t[j]==t[i]) ++j;
  14. f[i+1]=j;
  15. }
  16. REP(i,1,n) {
  17. int ok = 1;
  18. REP(j,0,m-1) if (s[i+j-m+1]!='?'&&s[i+j-m+1]!=t[j]) {
  19. ok = 0;
  20. break;
  21. }
  22. if (ok) {
  23. dp[i] = mx[i-m]+1;
  24. for (int j=f[m]; j; j=f[j]) {
  25. dp[i] = max(dp[i], dp[i-m+j]+1);
  26. }
  27. }
  28. mx[i] = max(dp[i], mx[i-1]);
  29. }
  30. printf("%d\n", mx[n]);
  31. }

kmp练习的更多相关文章

  1. KMP算法求解

    // KMP.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespac ...

  2. 简单有效的kmp算法

    以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...

  3. KMP算法

    KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...

  4. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  5. [KMP]【学习笔记】

    Oulipo Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 36916   Accepted: 14904 Descript ...

  6. KMP算法实现

    链接:http://blog.csdn.net/joylnwang/article/details/6778316 KMP算法是一种很经典的字符串匹配算法,链接中的讲解已经是很明确得了,自己按照其讲解 ...

  7. KMP专题

    1.[HDU 3336]Count the string(KMP+dp) 题意:求给定字符串含前缀的数量,如输入字符串abab,前缀是a.ab.aba.abab,在原字符串中出现的次数分别是2.2.1 ...

  8. KMP学习之旅

    说起kmp就要从字符串的匹配说起,下面我们谈谈字符串的匹配 给定一个原字符串:bababababababababb,再给定一个模式串:bababb,求模式串是否在源字符串中出现 最简单的方法就是遍历源 ...

  9. KMP模板

    参考:http://www.cnblogs.com/c-cloud/p/3224788.html #include<stdio.h> #include<string.h> vo ...

  10. 【字符串匹配】KMP算法和next数组的c/c++实现

    KMP算法基本思想有许多博客都写到了,写得也十分形象,不懂得可以参考下面的传送门,我就不解释基本思想了.本文主要给出KMP算法及next数组的计算方法(主要是很多网上的代码本人(相信应该是许多人吧)看 ...

随机推荐

  1. 20165207 Exp1 PC平台逆向破解

    20165207 Exp1 PC平台逆向破解 0.写在最前面 在做三个实验的前两个的时候,我还没有到博客里去看作业的要求.当时我的主机名是kali5207也就是用我的学号命名的,要求的是姓名全拼命名k ...

  2. Xcode10.x适配的部分问题

    因为我们项目是一个Workspace,由若干个库组成(组件化比较碎),又涉及到海外和国内(存在很多差异性),整个项目的编译是由每个库的脚本(每个库生成会支持32位和64位,每次编译前会清除历史缓存), ...

  3. C/C++之单例模式实现

    /*** * 保证一个类仅有一个实例,并提供一个访问它的全局访问点 */ #include <iostream> #include <string> using namespa ...

  4. Hive学习之路 (五)DbVisualizer配置连接hive

    一.安装DbVisualizer 下载地址http://www.dbvis.com/ 也可以从网上下载破解版程序,此处使用的版本是DbVisualizer 9.1.1 具体的安装步骤可以百度,或是修改 ...

  5. Centos 更改系统时间

    .date //查看本地 .hwclock --show //查看硬件的时间 .如果硬件的时间是对不上,那就对硬件的时间进行修改 .hwclock --set --date '2222-22-22 2 ...

  6. JavaScript Image对象 / Tabel对象 / Select对象 / Form对象

    JavaScript Image / Tabel / Select / Form 对象 版权声明:未经授权,严禁转载! Image 对象 Image 对象,代表 <img> 元素. < ...

  7. 20145118 《Java程序设计》课程总结

    20145118 <Java程序设计>课程总结 每周读书笔记连接汇总 假期笔记 http://www.cnblogs.com/cy1123/p/5224305.html 第一周读书笔记 h ...

  8. 20145310 《网络对抗》 MSF基础应用

    实验要求 掌握metasploit的基本应用方式,掌握常用的三种攻击方式的思路. 一个主动攻击,如ms08_067; 一个针对浏览器的攻击,如ms11_050: 一个针对客户端的攻击,如Adobe 成 ...

  9. 20165310 java_blog_week5

    # 2165310 <Java程序设计>第5周学习总结 教材学习内容总结 ch07内部类与异常类 内部类 - 继承外嵌类成员变量与方法 - 不可以声明类变量/类方法 - 不可以被外嵌类以外 ...

  10. C++ 表示一个区间值得方法

    C++中不允许这样的写法 85<= score <=100;你要想表示85<=score<=100的话只能这么写score>=85&&score<= ...