kmp练习
kmp板子如下, 失配数组不优化的话, $f_i$就表示子串[0...i]前后缀最大匹配长度
int main() {
scanf("%s%s", t, p);
int n = strlen(t), m = strlen(p);
f[0]=f[1]=0;
int j = 0;
REP(i,1,m-1) {
while (j&&p[i]!=p[j]) j=f[j];
if (p[i]==p[j]) ++j;
f[i+1] = j;
}
j = 0;
REP(i,0,n-1) {
while (j&&p[j]!=t[i]) j=f[j];
if (p[j]==t[i]) ++j;
if (j==m) printf("%d", i-m+1);
}
}
练习1: hdu5763
大意: 给定字符串T, 模板串P, 可以将T中与P匹配的子串替换为'*', 求多少种替换方案.
一个板子题, kmp求出可以替换的位置, 然后dp就好了
const int N = 1e6+10;
char t[N], p[N];
int f[N], ff[N], *dp=ff+1;
void add(int &a, int b) {a+=b;if (a>=P) a-=P;} void work() {
scanf("%s%s", t, p);
int n = strlen(t), m = strlen(p);
f[0]=f[1]=0;
REP(i,1,m-1) {
int j = f[i];
while (j&&p[i]!=p[j]) j=f[j];
if (p[i]==p[j]) ++j;
f[i+1] = j;
}
int j = 0;
dp[-1] = 1;
REP(i,0,n-1) {
while (j&&p[j]!=t[i]) j=f[j];
if (p[j]==t[i]) ++j;
dp[i] = 0;
if (j==m) add(dp[i],dp[i-m]);
add(dp[i],dp[i-1]);
}
printf("%d\n",dp[n-1]);
} int main() {
int t;
scanf("%d", &t);
REP(i,1,t) printf("Case #%d: ",i),work();
}
练习2 CF825F
大意: 给定字符串$s$, 可以将连续$c1$个相同的子串$s1$压缩为|c1|+|s1|, 求压缩若干次后$s$最短长度.
要用到一个字符串循环节的结论, 假设一个长$n$的字符串$s$, 失配函数为$f$, 则循环节为n-f[n]或n
const int N = 8e3+10;
int n;
char s[N];
int f[N], g[N][N], ff[N], *dp=ff+1;
void getFail(char *s) {
int m = strlen(s);
f[0]=f[1]=0;
REP(i,1,m-1) {
int j=f[i];
while (j&&s[i]!=s[j]) j=f[j];
if (s[i]==s[j]) ++j;
f[i+1] = j;
}
}
int calc(int x) {int r=0;while (x) ++r,x/=10;return r;} int main() {
scanf("%s", s);
n = strlen(s);
REP(i,0,n-1) {
getFail(s+i);
REP(j,i,n-1) {
int len = j-i+1;
if (len%(len-f[len])==0) g[i][j]=len-f[len]+calc(len/(len-f[len]));
else g[i][j]=len+1;
}
}
dp[-1] = 0;
REP(i,0,n-1) {
dp[i] = INF;
REP(j,0,i) dp[i] = min(dp[i], dp[j-1]+g[j][i]);
}
printf("%d\n", dp[n-1]);
}
练习3 CF494B
这题想了好长时间, 计数还是不熟练啊
$dp[i]=j+1+\sum\limits_{k\le j-1} sum[k]$
dp[i]是以i为右端点的方案, sum[i]是dp[i]的前缀和, j为上次匹配位置
int n, m;
char t[N], p[N];
int dp[N], s1[N], s2[N], f[N], g[N];
void add(int &a, int b) {a+=b;if (a>=P)a-=P;} int main() {
scanf("%s%s", t, p);
n = strlen(t), m = strlen(p);
f[0]=f[1]=0;
REP(i,1,m-1) {
int j=f[i];
while (j&&p[j]!=p[i]) j=f[j];
if (p[j]==p[i]) ++j;
f[i+1]=j;
}
int j=0;
REP(i,0,n-1) {
while (j&&t[i]!=p[j]) j=f[j];
if (t[i]==p[j]) ++j;
if (j==m) g[i]=1;
}
REP(i,0,n-1) {
if (g[i]) j=i-m+1,dp[i]=s2[j-1],add(dp[i],j+1);
else if (i) dp[i] = dp[i-1];
if (i) s1[i] = s1[i-1];
add(s1[i],dp[i]);
if (i) s2[i] = s2[i-1];
add(s2[i],s1[i]);
}
printf("%d\n",s1[n-1]);
}
练习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)$实际上也能跑过.
const int N = 210;
int n, m;
char s[N];
int dp[N][N][N], f[N]; void add(int &a, int b) {a+=b;if (a>=P)a-=P;} int main() {
scanf("%d%s", &n, s);
m = strlen(s);
REP(i,1,m-1) {
int j = f[i];
while (j&&s[i]!=s[j]) j=f[j];
if (s[i]==s[j]) ++j;
f[i+1]=j;
}
REP(i,1,m-1) if (s[i]==s[f[i]]) f[i]=f[f[i]];
dp[0][0][0]=1;
REP(i,1,2*n) REP(j,0,m) REP(k,0,n) if (dp[i-1][j][k]) {
int nxt = j;
while (nxt&&s[nxt]!='(') nxt=f[nxt];
if (s[nxt]=='(') ++nxt;
if (j==m) nxt=m;
add(dp[i][nxt][k+1],dp[i-1][j][k]);
if (!k) continue;
nxt = j;
while (nxt&&s[nxt]!=')') nxt=f[nxt];
if (s[nxt]==')') ++nxt;
if (j==m) nxt=m;
add(dp[i][nxt][k-1],dp[i-1][j][k]);
}
printf("%d\n", dp[2*n][m][0]);
}
练习5 HDU3689 Infinite monkey theorem
跟上题类似, 借助kmp进行转移
const int N = 1e3+10;
int n, m;
double q[N], dp[N][22];
char s[N];
int f[N]; void work() {
REP(i,'a','z') q[i]=0;
REP(i,1,n) {
char c;
double x;
scanf(" %c%lf", &c, &x);
q[c] = x;
}
scanf("%s", s);
n = strlen(s);
REP(i,1,n-1) {
int j=f[i];
while (j&&s[j]!=s[i]) j=f[j];
if (s[j]==s[i]) ++j;
f[i+1]=j;
}
REP(i,1,n-1) if (s[i]==s[f[i]]) f[i]=f[f[i]];
memset(dp,0,sizeof dp);
dp[0][0] = 1;
REP(i,1,m) REP(j,0,n) {
REP(k,'a','z') {
int nxt = j;
while (nxt&&s[nxt]!=k) nxt=f[nxt];
if (s[nxt]==k) ++nxt;
if (j==n) nxt=n;
dp[i][nxt] += q[k]*dp[i-1][j];
}
}
printf("%.2lf%%\n", 100*dp[m][n]);
} int main() {
for (; scanf("%d%d", &n, &m), n||m; ) work();
}
练习6 CF 432D Prefixes and Suffixes
大意: 给定字符串$s$, 求$s$有多少个前缀等于后缀, 并且输出它们的出现次数
kmp经典题了, 失配函数如果不优化的话, 那么$f_i$就表示子串[0,i-1]中最大的使前缀等于后缀的前缀长度.
也就是说$s$满足要求的最大前缀长度是$f[n]$, 其余前缀一定在$f[n]$内, 这样就可以迭代求出满足要求的前缀位置.
再用$dp$求出每个前缀的出现次数即可.
const int N = 1e6+10;
int n;
char s[N];
int f[N], dp[N], a[N]; int main() {
scanf("%s", s);
n = strlen(s);
REP(i,1,n-1) {
int j = f[i];
while (j&&s[i]!=s[j]) j=f[j];
if (s[i]==s[j]) ++j;
f[i+1]=j;
}
for (int nxt=n; nxt; nxt=f[nxt]) a[++*a]=nxt;
PER(i,1,n) dp[f[i]]+=++dp[i];
printf("%d\n", *a);
PER(i,1,*a) printf("%d %d\n", a[i], dp[a[i]]);
}
练习7 CF808G Anthem of Berland
先预处理了一下第i位是否能匹配到第j位, 最后再dp一遍
const int N = 1e6+10;
int n, m;
char s[N], t[N];
vector<vector<int> > g;
int dp[N], mx[N], f[N], ff[N]; int main() {
scanf("%s%s", s+1, t);
n = strlen(s+1), m = strlen(t);
if (n<m) return puts("0"),0;
REP(i,0,n+10) g.pb(vector<int>());
REP(i,0,n+10) REP(j,0,m+10) g[i].pb(0);
REP(i,1,m-1) {
int j = f[i];
while (j&&t[j]!=t[i]) j=f[j];
if (t[j]==t[i]) ++j;
f[i+1]=j;
}
memcpy(ff,f,sizeof ff);
REP(i,1,m-1) if (t[i]==t[f[i]]) f[i]=f[f[i]];
REP(i,0,n) g[i][0] = 1;
REP(i,1,n) REP(j,0,m) if (g[i-1][j]) {
int nxt = j;
while (nxt&&t[nxt]!=s[i]) nxt=f[nxt];
if (t[nxt]==s[i]) ++nxt;
g[i][nxt] = 1;
if (s[i]=='?') g[i][j+1] = 1;
}
REP(i,1,n) {
if (g[i][m]) {
dp[i] = mx[i-m]+1;
for (int j=ff[m]; j; j=ff[j]) {
dp[i] = max(dp[i], dp[i-m+j]+1);
}
}
mx[i] = max(dp[i], mx[i-1]);
}
printf("%d\n", mx[n]);
}
看了下别人题解后发现自己想复杂了....只需要判断是否能匹配到$m$位, 暴力判断就好了, 并且复杂度会少一个$\omega$,但实际跑了下没差多少.
const int N = 1e6+10;
int n, m;
char s[N], t[N];
int dp[N], mx[N], f[N]; int main() {
scanf("%s%s", s+1, t);
n = strlen(s+1), m = strlen(t);
if (n<m) return puts("0"),0;
REP(i,1,m-1) {
int j = f[i];
while (j&&t[j]!=t[i]) j=f[j];
if (t[j]==t[i]) ++j;
f[i+1]=j;
}
REP(i,1,n) {
int ok = 1;
REP(j,0,m-1) if (s[i+j-m+1]!='?'&&s[i+j-m+1]!=t[j]) {
ok = 0;
break;
}
if (ok) {
dp[i] = mx[i-m]+1;
for (int j=f[m]; j; j=f[j]) {
dp[i] = max(dp[i], dp[i-m+j]+1);
}
}
mx[i] = max(dp[i], mx[i-1]);
}
printf("%d\n", mx[n]);
}
kmp练习的更多相关文章
- KMP算法求解
// KMP.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> using namespac ...
- 简单有效的kmp算法
以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下“奥,它是做模式匹配的”这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(先不说 ...
- KMP算法
KMP算法是字符串模式匹配当中最经典的算法,原来大二学数据结构的有讲,但是当时只是记住了原理,但不知道代码实现,今天终于是完成了KMP的代码实现.原理KMP的原理其实很简单,给定一个字符串和一个模式串 ...
- 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)
前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...
- [KMP]【学习笔记】
Oulipo Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 36916 Accepted: 14904 Descript ...
- KMP算法实现
链接:http://blog.csdn.net/joylnwang/article/details/6778316 KMP算法是一种很经典的字符串匹配算法,链接中的讲解已经是很明确得了,自己按照其讲解 ...
- KMP专题
1.[HDU 3336]Count the string(KMP+dp) 题意:求给定字符串含前缀的数量,如输入字符串abab,前缀是a.ab.aba.abab,在原字符串中出现的次数分别是2.2.1 ...
- KMP学习之旅
说起kmp就要从字符串的匹配说起,下面我们谈谈字符串的匹配 给定一个原字符串:bababababababababb,再给定一个模式串:bababb,求模式串是否在源字符串中出现 最简单的方法就是遍历源 ...
- KMP模板
参考:http://www.cnblogs.com/c-cloud/p/3224788.html #include<stdio.h> #include<string.h> vo ...
- 【字符串匹配】KMP算法和next数组的c/c++实现
KMP算法基本思想有许多博客都写到了,写得也十分形象,不懂得可以参考下面的传送门,我就不解释基本思想了.本文主要给出KMP算法及next数组的计算方法(主要是很多网上的代码本人(相信应该是许多人吧)看 ...
随机推荐
- 数据仓库基础(六)数据的ETL
本文转载自:http://www.cnblogs.com/evencao/p/3140487.html ETL是数据抽取(Extract).转换(Transform).加载(Load)的简写,是构建数 ...
- SNMP学习笔记之SNMP TRAP简介、流程以及使用Python实现接受Trap信息
0x00 SNMP TRAP简介 SNMP(Simple Network Management Protocol) trap是一种很有用,但是也容易让人难以理解的协议. 虽然名字叫做简单网络管理协议, ...
- P3456 [POI2007]GRZ-Ridges and Valleys(bfs)
P3456 [POI2007]GRZ-Ridges and Valleys 八个方向都跑一遍bfs,顺便判断一下是山峰还是山谷,或者是山坡(俩都不是) (实在不知道要说啥了qwq) #include& ...
- 20145327 《网络对抗》MSF基础应用
20145327 <网络对抗>MSF基础应用 主动攻击ms08_067 两台虚拟机,其中一台为kali,一台为windows xp sp3(英文版) kali ip地址:192.168.4 ...
- linux内核分析 第四周
一.系统调用的三层皮 内核态.用户态 Intel x86 CPU有四个权限分级,0-3.Linux只取两种,0是内核态,3是用户态. 0xc0000000以上的空间只能在内核态下访问 0x000000 ...
- Timer,TimerTask,Handler
新建一个定时器线程,通过此线程每一秒发送数据到Handler,然后通过Handler来修改UI. 1.获得Handler,Timer,TimerTask对象. Handler handler=new ...
- POJ 2348 Euclid's Game(博弈)题解
题意:有a,b两个数字,两人轮流操作,每次可以选择两个之中较小的数字,然后另一个数字减去选择数字的任意倍数(不能减到负数),直到其中一个为0,不能操作为败 思路:这题用博弈NP思想,必败点和必胜点之间 ...
- 【TCP/IP详解 卷一:协议】第二十四章 TCP的未来与性能
来到了TCP的最后一个章节,未来与性能.在当时(1991年)的未来,如今已经部分变为现实,部分就只是历史中的实验. 主要内容: 路径MTU的发现与TCP的结合. 长肥管道 和 高速千兆比网络. 窗口扩 ...
- HDU 5889 Barricade(最短路+最小割)
http://acm.hdu.edu.cn/showproblem.php?pid=5889 题意: 给出一个图,帝国将军位于1处,敌军位于n处,敌军会选择最短路到达1点.现在帝国将军要在路径上放置障 ...
- redis nginx session tomcat
Redis解决session共享 http://lyl-zsu.iteye.com/blog/2408292 http://zx10103326.iteye.com/blog/2244195 http ...