KMP 字符串匹配
kmp举例
- 给定字符串A和字符串B,请判断B是否是A的子串(即:A串是否包含B串)
- 比如,字符串A=“Dingba hen shuai!” 字符串B=“shuai”
- A串(等待匹配的串),称为主串(母串)
- B串(用来匹配的串),称为模式串

发现 当找到6不匹配时,j减少,相当于向后拉:


P[]这个数组是当前j位置的前缀和后缀的相同最大长度,便于移动 j

KMP代码
核心
注意:必须使用1开始的下标
int ans=0, j=0; for(int i=0; i<n; i++) { while(j>0 && B[j+1]!=A[i+1]) j=P[j]; if(B[j+1]==A[i+1]) j++; if(j==m) { printf("%d\n", i-m); j=P[j]; } }
|
上面的代码允许重叠
如果不允许重叠:
j=0即可;
P[]的处理
相当于错位的KMP,对自己做kmp

KMP算法-P数组的预处理
void pre() { P[1]=0; int j=0; for( int i=1; i<m; i++ ) { while(j>0 && B[j+1]!=B[i+1]) j=P[j]; if(B[j+1]==B[i+1]) j++; P[i+1]=j; } }
|
小结
一句话理解算法:
扫描字符串A,并更新可以匹配到B的什么位置!
例1-花布条
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
每组数据占一行,用空格隔开的2个字符串,分别表示花布条和小饰条,'#'结束。
对于每组数据,输出一个整数,表示能从花布条中剪出的最多小饰条的块数。
样例输入
abcde a3
aaaaaa aa
#
样例输出
0
3
解题代码:
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define ll long long
char s1[1010]; char s2[1010];
int p[1010];
void pre(){ int j=0; p[1]=0; int m=strlen(s2+1); for(int i=1;i<m;i++){ while(j>0 && s2[i+1]!=s2[j+1]){ j=p[j]; } if(s2[i+1]==s2[j+1]){ j++; } p[i+1]=j; } return ; }
int kmp(){ int ans=0; int n=strlen(s1+1); int m=strlen(s2+1); int j=0; for(int i=0;i<=n;i++){ while(j>0 && s1[i+1]!=s2[j+1]){ j=p[j]; } if(s1[i+1]==s2[j+1]){ j++ ; } if(j==m){ ans++; j=0; } } return ans; }
int main(){ while(cin>>(s1+1),s1[1]!='#'){ cin>>(s2+1); memset(p,0,sizeof(p)); pre(); for(int i=0;i<=strlen(s2+1);i++){ cout<<p[i]<<" "; } cout<<endl; cout<<"ANS::"; int ans=kmp(); cout<<ans<<endl; } return 0; }
|
例2-pre
给定字符串S1和S2,请查找字符串S1的前缀中同时也是S2的后缀的最大长度是多少。
riemann
marjorie
rie 3
S1+S2求解P值,同时不能超过各个串的长度最小值。
代码:
#include <cstdio> #include <iostream> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define ll long long
int p[100010]; string s1; string s2; int maxn=0;
void pre(){ memset(p,-1,sizeof(p)); int j=-1; int n=s1.length(); for(int i=0;i<n-1;i++){ while(j>=0 && s1[i+1]!=s1[j+1]){ j=p[j]; } if(s1[i+1]==s1[j+1]){ j++; } p[i+1]=j; } }
int main(){ while(cin>>s1>>s2){ maxn=min(s1.length(),s2.length()); s1+=s2; pre(); int x=min(maxn,p[s1.length()-1]+1); for(int i=0;i<x;i++){ cout<<s1[i]; } cout<<" "<<x<<endl; } return 0; }
|