字符串匹配
BF算法(朴素模式匹配)
时间复杂度O(m*n),普通的模式匹配算法
BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;
若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。
模板
输出s2在s1里出现的次数
我写的,两行获取两行字符,第二行的字符串在第一行的字符串匹配,看看有几个
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn=1e6+5; int main(){ char str[maxn]; gets(str); char s[maxn]; gets(s); int len1=strlen(str); int len2=strlen(s); int cnt=0; for(int i=0;i<len1;i++){ if(str[i]==s[0]){ int flag=1; for(int j=0;j<len2;j++){ if(str[i+j]==s[j]){ }else{ flag=0; break; } } if(flag)cnt++; } } printf("%d\n",cnt); return 0; }
匹配成功,继续,匹配失败回溯
输出第一次匹配的下标
#include <iostream> #include <string.h> using namespace std; int BF(char a[],char b[]){ int index=0;//第一个字符串的下标 int i=0;//第一个字符串目前下标 int j=0;//第二个字符串目前下标 int len1=strlen(a); int len2=strlen(b); while(i!=len1&&j!=len2){ if(a[i]==b[j]){ i++; j++; }else{ index++; i=index; j=0; } } if(j==len2) return index+1;//匹配成功 else return 0; } int main(){ char a[200]; cout<<"请输入主串:"; cin>>a; char b[200]; cout<<"请输入子串:"; cin>>b; cout<<"串在主串首次匹配的位置是:"<<BF(a,b)<<endl; return 0; }
kmp算法---部分匹配表(Partial Match Table)
时间复杂度O(m+n)
前缀:指的是字符串的子串中从原串最前面开始的子串,如abcdef的前缀有:a,ab,abc,abcd,abcde
后缀:指的是字符串的子串中在原串结尾处结尾的子串,如abcdef的后缀有:f,ef,def,cdef,bcdef
kmp算法最大的特点是加入了next[i]数组
next[i](i从1开始算)代表着,除去第i个数,在一个字符串里面从第一个数到第(i-1)字符串前缀与后缀最长重复的个数。
在“aba”中,前缀是“ab”,后缀是“ba”,那么两者最长的子串就是“a”;
在“ababa”中,前缀是“abab”,后缀是“baba”,二者最长重复子串是“aba”;
在“abcabcdabc”中,前缀是“abcabcdab”,后缀是“bcabcdabc”,二者最长重复的子串是“abc”;
next[1] = -1,代表着除了第一个元素,之前前缀后缀最长的重复子串,这里是空 ,即"",没有,我们记为-1,代表空。(0代表1位相同,1代表两位相同,依次累加)。
next[2] = -1,即“a”,没有前缀与后缀,故最长重复的子串是空,值为-1;
next[3] = -1,即“ab”,前缀是“a”,后缀是“b”,最长重复的子串“”;
next[4] = 1,即"aba",前缀是“ab”,后缀是“ba”,最长重复的子串“a”;next数组里面就是最长重复子串字符串的个数
next[5] = 2,即"abab",前缀是“aba”,后缀是“bab”,最长重复的子串“ab”;
next[6] = 3,即"ababa",前缀是“abab”,后缀是“baba”,最长重复的子串“aba”;
next[7] = 1,即"ababaa",前缀是“ababa”,后缀是“babaa”,最长重复的子串“a”;
next[8] = 1,即"ababaaa",前缀是“ababaa”,后缀是“babaaa”,最长重复的子串“a”;
next[9] = 2,即"ababaaab",前缀是“ababaaa”,后缀是“babaaab”,最长重复的子串“ab”;
next[10] = 3,即"ababaaaba",前缀是“ababaaab”,后缀是“babaaaba”,最长重复的子串“aba”;
next[11] = 4,即"ababaaabab",前缀是“ababaaaba”,后缀是“babaaabab”,最长重复的子串“abab”;
next[12] = 5,即"ababaaababa",前缀是“ababaaabab”,后缀是“babaaaababa”,最长重复的子串“ababa”;
首先开始计算主串与子串的字符,设置主串用i来表示,子串用j来表示,如果ptr[i]与a[i]相等,那么i与j就都加1:
模板
https://www.luogu.org/problem/P3375
输出匹配的首字母下标和next[]数组
#include <iostream> #include <cstdio> #include <cstring> using namespace std; char a[1000005]; char b[1000005]; int next[1000005]; int len1,len2; void getnext(){ int p=0;//p是下标 next[1]=0; for(int i=2;i<=len2;i++){ while(p&&b[i]!=b[p+1]){ p=next[p]; } if(b[p+1]==b[i])p++; next[i]=p; } } void kmp(){ int p=0; for(int i=1;i<=len1;i++){ while(p&&b[p+1]!=a[i])p=next[p]; if(b[p+1]==a[i])p++; if(p==len2){ printf("%d\n",i-len2+1); p=next[p];//改成p=0的话是不重复 } } for(int i=1;i<=len2;i++)printf("%d ",next[i]); } int main(){ scanf("%s",a+1); scanf("%s",b+1); len1=strlen(a+1); len2=strlen(b+1); getnext(); kmp(); return 0; }
例题
HDU2087剪花布条--要求匹配不重复
http://acm.hdu.edu.cn/showproblem.php?pid=2087
#include <iostream> #include <cstdio> #include <cstring> using namespace std; char a[1000005]; char b[1000005]; int Next[1000005]; int len1,len2; int cnt; void getnext(){ int p=0; Next[1]=0; for(int i=2;i<=len2;i++){ while(p&&b[i]!=b[p+1]){ p=Next[p]; } if(b[p+1]==b[i])p++; Next[i]=p; } } void kmp(){ int p=0; for(int i=1;i<=len1;i++){ while(p&&b[p+1]!=a[i])p=Next[p]; if(b[p+1]==a[i])p++; if(p==len2){ cnt++; p=0;//达到不重复的目的 } } } int main(){ while(1){ cnt=0; scanf("%s",a+1); if(strcmp(a+1,"#")==0)break; scanf("%s",b+1); len1=strlen(a+1); len2=strlen(b+1); getnext(); kmp(); printf("%d\n",cnt); } return 0; }
HDU1711Number Sequence---输出匹配的第一个位置
http://acm.hdu.edu.cn/showproblem.php?pid=1711
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int a[1000005]; int b[1000005]; int Next[1000005]; int len1,len2; int cnt; int flag=-1; void getnext(){ int p=0; Next[1]=0; for(int i=2;i<=len2;i++){ while(p&&b[i]!=b[p+1]){ p=Next[p]; } if(b[p+1]==b[i])p++; Next[i]=p; } } void kmp(){ int p=0; for(int i=1;i<=len1;i++){ while(p&&b[p+1]!=a[i])p=Next[p]; if(b[p+1]==a[i])p++; if(p==len2){ flag=i-len2+1; p=Next[p];// return; } } } int main(){ int t; cin>>t; while(t--){ flag=-1; int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int j=1;j<=m;j++){ scanf("%d",&b[j]); } len1=n; len2=m; getnext(); kmp(); printf("%d\n",flag==-1?-1:flag); } return 0; }