字符串匹配
问题描述
-
输入两个字符串A和B;
-
输出字符串B在字符串A中的位置。
示例
-
输入字符串“ABDABACDBD”和字符串“BACD”;
-
输出偏移量5,或输出在第一个字符串中“BACD”处的指针。
方法
-
常用方法有好多:朴素算法,Rabin-Karp算法,有限自动机算法,KMP算法。
-
复杂度对比:复杂度为预处理时间和匹配时间的和。
算法 预处理时间 匹配时间 朴素算法 0 Ο((n-m+1)*m) Rabin-Karp算法 Θ(m) Ο((n-m+1)*m) 有限自动机算法 Ο(m*字符集个数) Θ(m) Knuth-Morris-Prattu算法 Θ(m) Θ(m) Boyer–Moore算法 待搜索 待搜索
朴素算法
-
通过循环找到所有有效偏移;
-
函数NaiveStringMatcher(char *A,char *T)实现在字符串A中匹配字符串T;
-
辅助函数CharacterMatcher(char *A,char *B,char *T)判断指针A和B之间字符串是不是和字符串T匹配。
bool CharacterMatcher(char *A,char *B,char *T) { char *cc = A; int i = 0; while(cc != B){ if(*cc != *(T+i)) return false; cc++; i++; } return true; } void NaiveStringMatcher(char *A,char *T) { int iLengthofA = strlen(A); int iLengthofT = strlen(T); for(int i = 0;i <= iLengthofA-iLengthofT ;i++){ if(CharacterMatcher(A+i,A+i+iLengthofT,T)) cout<<"Pattern occurs with shift"<< i+1 << endl; } }
Rabin-Karp算法1
-
将字符串做一种数的运算得到一个值,然后进行值的比较;
-
这个数值要求可以表示出字符的前后顺序,而且可以随时去掉某个字符的值,可以随时添加一个新字符的值。
-
例如A="12345",B="34",对于B采用运算(3*10+4)得到34作为B的值;采用相同运算方式对A进行运算,然后做比较。
-
如果相乘过大时,可以对一个较大的素数求模,然后比较。
double Encryption(char *A, int m,int q) { int i =0; double sum = 0.0; while(i < m){ sum = sum * q + ((int)*(A + i++)) ; } return sum; } int ChangeEncryption(char *A,int ll,int sum,int q) { double mm = pow((double)q,(double)ll - 1); return ((sum - (int)mm * (*A)) * q + *(A + ll)); } char * Rabin_Karp_Algo(char *A,char *B) { int ll = strlen(B); double m = Encryption(B ,ll, 128); double n = Encryption(A ,ll, 128); char *cc = A; for(;*(cc+ll) != '\0';cc++){ if(n == m){ return cc; } n = ChangeEncryption(cc,ll,n,128); } return NULL; }
有限自动机算法2
-
在字符串A中搜索模式T,对于模式T中出现的所有可能情况看作不同状态。
-
如模式T为“ababaca”
-
建立状态0~7,分别对应{初始状态}、{a}、{ab}、{aba}、{abab}、{ababa}、{ababac}、{ababaca}。
-
计算对应状态转移表(计算方法后面讲到)。
static int delta[8][4]={ //转移函数 检测ababaca //a,b,c,*. {1,0,0,0}, //状态0 {初始状态} {1,2,0,0}, //状态1 {a,} {3,0,0,0}, //状态2 {ab,} {1,4,0,0}, //状态3 {aba,} {5,0,0,0}, //状态4 {abab,} {1,4,6,0}, //状态5 {ababa,} {7,0,0,0}, //状态6 {ababc,} {1,2,0,0} //状态7 {ababca,} }; int State = 0; //初试状态
-
函数AutomationMatch(char *A)为匹配函数,返回位置指针。
char *AutomationMatch(char *A) { int Length = strlen(A); char *cc = A; while(*cc != '\0'){ int n = *cc++ - 'a'; if(n != 0 && n != 1 && n != 2) n = 3; State = delta[State][n]; if(State == 7) return cc-7; } return NULL; }
-
状态转移表计算
-
根据给定模式T[1..m]来计算。
-
伪代码为
COMPUTE-TRANSITION-FUNCTION(T,E) 1 m=P.length 2 for q = 0 to m 3 for each charater a∈E 4 k = MIN(m+1,q+1) 5 repeat 6 k = k - 1 7 until T[1..k] is tail of T[1..q]&a 8 δ(q,a) = k 9 return δ
-
c++代码
bool TT(char *T,int k,int q,char a) { if(*(T + k-- - 1) != a) return false; while(k > 0 && q > 0){ if(*(T + k - 1) != *(T + q - 1)) return false; k--; q--; } return true; } int *TransitionFunction(char *T,char *E) //T为搜索模式,E为T的字符集 { const int m = strlen(T); const int n = strlen(E); int *Ans[8][3]; int q = 0,p = 0; int k = 0; for(;q <= m;q++){ for(p = 0;p < n;p++){ k = MIN(m+1,q+1); while(!TT(T,k,q,*(E+p)) && k>0){ k = k-1; } *((int*)Ans + n * q + p) = k; } } return (int*)Ans; }
KMP算法3
-
详细解释看参考
-
代码
int *ComputePrefixFunction(char *P) { int i = 1; int j = 0; int m = strlen(P); int *s = new int[m]; memset(s,0,m * 8); while(i < m){ while(P[i] == P[j] && i < m ){ s[i] = s[i-1] + 1; j++; i++; } j = 0; i++; } return s; } void KMPMatcher(char *T,char *P) { int n = strlen(T); int m = strlen(P); int q = 0; int *Map = ComputePrefixFunction(P); int i; for(i = 0 ; i < n ; i++){ while(q > 0 && P[q] != T[i]) q = Map[q-1]; if(P[q] == T[i]) q++; if(q == m){ int gg = i -m +1 ; cout << "zhongduan:" << ends; printf("%d",gg); q = Map[q - 1]; } } }
by:狼儿乖乖
time:2015/1/19 20:51:43