博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
字符串匹配
阅读量:7114 次
发布时间:2019-06-28

本文共 4360 字,大约阅读时间需要 14 分钟。

字符串匹配一直是比较重要的一个要点,可以有很多种算法实现,最好的当然就是比较有名的KMP算法了,下面把算法导论上的所有算法都实现一遍。

一:朴素字符串匹配算法

就是最简单的思路,两个递归遍历一遍,复杂度是O((n-m+1)*m)

二:Rabin—Karp算法

基本思路是将字符串的每个字符转化为对应的数字,比如“5651”就对应数5651,这样只要每次算出数,进行数的比较即可。它的最坏复杂度也是O((n-m+1)*m),但是平均情况要好很多。

/*如果出现很大的数可以通过mod一个素数来实现,**我这里只是进行了简单的实现*/#define __DEBUG 0#if __DEBUG	#define DEBUG(fmt,...) printf(fmt,__VA_ARGS__)#else	#define DEBUG(fmt,...)#endif#include
#include
#include
#include
using namespace std;const int maxn = 5000;char t[maxn],p[maxn];int s[maxn];int d, h, pt,q;int n, m;int quick_pow(int a, int b){ int sum = 1; while (b) { if (b % 2 == 1) sum = sum*a; a =a*a; b >>= 1; } return sum;}int main(){ q = 13, d = 10; scanf("%s%s", t,p); n = strlen(t); m = strlen(p); int h = quick_pow(d, m - 1); DEBUG("%d\n", h); pt = 0; s[0] = 0; for (int i = 0; i < m; i++) { pt = (d*pt + p[i] - '0'); s[0] = (d*s[0] + t[i] - '0'); } DEBUG("%d %d\n", pt, s[0]); for (int i = 0; i <= n - m; i++) { if (pt == s[i]) printf("Pattern occurs with shift %d\n",i); else if (i < n - m) s[i + 1] = d*(s[i] - (t[i] - '0')*h) + t[i + m] - '0'; } return 0;}

三:有限自动机实现字符串匹配

使用有限自动机,先将所有的状态转移写成状态转移图,然后进行处理即可。算法导论上写的比较难理解。时间复杂度的话匹配部分是O(n),预处理部分是O(m^3|∑|),可以改进为O(m|∑|),所以总和为O(m|∑|)+O(n)。

基本思路参照这个博客:我自己理解的时候主要是这个状态转移图中的回溯边想不清楚,搞了很长时间才明白:状态转移图与T无关,只要考虑P,因为当前P一定是当前T的后缀,所以假如现在P为ab,新加入字符a如果匹配,就沿着主线+1即可;如果不匹配,将a加到ab后形成aba,寻找它最长的前缀使其与aba后缀相等,这里就是a,这样就返回状态1,也就是现在T的新后缀是aba,那么原本的ab就不能用了,重新从a开始,也就是状态1。确实比较难理解。

#include
#include
#include
#include
#include
using namespace std;bool MatchPerfix(const char *P, int k, int q, char a){ if (k == 0) return true; if (k == 1) return P[k - 1] == a; return P[k - 1] == a&&strncmp(P, P + q - k + 1, k - 1)==0;//找出最长的P中前缀==后缀的子串}vector
> ComputeTransionFunction(const char *P, const char * alpha)//产生状态转移图{ int n = strlen(P); int j = 0; int k = 0; vector
> transion_map(n + 1); for (int q = 0; q <=n; q++)//代表P的第q的字符进行比较 { j = 0; while (alpha[j] != '\0') { k = min(n, q + 1);//k作为状态 while (!MatchPerfix(P, k, q, alpha[j])) { k--; } transion_map[q][alpha[j]] = k; j++; } } return transion_map;}void PrintTransionMap(vector
> transion_map)//输出状态转移图{ typedef vector
>::iterator VCI; typedef map
::iterator MCI; for (VCI iter = transion_map.begin(); iter != transion_map.end(); iter++) { map
m = *iter; for (MCI p = m.begin(); p != m.end(); p++) { cout << p->first << "--" << p->second << " "; } cout << endl; }}void FiniteAutomatonMatcher(const char *P, const char *T, const char *alpha){ int n = strlen(P); int m = strlen(T); int q = 0; vector
> transion_map = ComputeTransionFunction(P, alpha); //PrintTransionMap(transion_map); for (int i = 0; i < m; i++) { q = transion_map[q][T[i]]; if (q == n) { cout << "The pattern occures with shift " << i - n + 1 << endl; } }}int main(){ const int max_lengh = 1000; const char *alpha = "abcdefghijklmnopqrstuvwxyz"; char P[max_lengh]; char T[max_lengh]; while (gets_s(T)) { gets_s(P); FiniteAutomatonMatcher(P, T, alpha); cout << "Enter next case\n"; } return 0;}

四:KMP算法

kmp算法是字符串匹配的最佳算法了,时间复杂度是O(n),真是强无敌。基本思路跟有限自动机一样,都是先找出状态转移图,也就是每个长度对应的最长真前缀,比有限自动机好的地方在于,有限自动机需要把字符串每个长度对应的所有出现过的字符的状态转移存储,而KMP只要简单的进行P中状态转移即可,简单很多。

#include
#include
#include
using namespace std;const int maxn = 1500;void ComputePrefixFunction(char P[],int next[]){ int m = strlen(P); memset(next, 0, sizeof(next)); next[0] = 0; int k = 0; //k作为前缀最长长度 for (int q = 1; q < m; q++) { while (k > 0 && P[k] != P[q]) k = next[k-1];//如果新的一个字符不能与+1后的k匹配,就要找当前前缀的前缀 if (P[k] == P[q]) k++; next[q] = k; }}bool KMPMatcher(char T[], char P[], int next[]){ int n = strlen(T); int m = strlen(P); ComputePrefixFunction(P, next); int q = 0; for (int i = 0; i < n; i++) { while (q > 0 && P[q] != T[i])//最新的一个字符不能匹配,寻找当前匹配子串的最长真前缀 q = next[q - 1]; if (P[q] == T[i]) q++; if (q == m) { cout << "Pattern occurs with shift : " << i - m + 1 << endl; return true; } } return false;}int main(){ int next[maxn]; char P[maxn], T[maxn]; while (cin>>T) { cin >> P; if (!KMPMatcher(T, P, next)) cout << "There is no catch" << endl; cout << "Enter next case" << endl; } return 0;}

转载于:https://www.cnblogs.com/seasonal/p/10343671.html

你可能感兴趣的文章
Hmily 2.0.3 发布,高性能异步分布式事务 TCC 框架
查看>>
烟花年下岁月
查看>>
Java源码阅读之HashMap - JDK1.8
查看>>
JavaScript 工厂模式和订阅模式
查看>>
阮一峰老师微博上的关于js作用域的一道题
查看>>
关于两个程序员的寓言故事
查看>>
Docker 构建统一的前端开发环境
查看>>
一文让你了解大数据时代,你的真实处境
查看>>
Problems at works
查看>>
Dell服务器系统安装后无法正常进入系统
查看>>
深入理解asp.net里的HttpModule机制
查看>>
java基础学习_常用类03_StringBuffer类、数组高级和Arrays类、Integer类和Character类_day13总结...
查看>>
Asp.net MVC Session过期异常的处理
查看>>
python ThreadPoolExecutor线程池使用
查看>>
IPTABLES 规则(Rules)
查看>>
关于URL编码
查看>>
深度学习的可解释性研究(一):让模型「说人话」
查看>>
QT5提示can not find -lGL的解决方法
查看>>
Silverlight/Windows8/WPF/WP7/HTML5周学习导读(9月17日-9月23日)
查看>>
Tap-Ahead:让移动搜索更加便捷的解决之道
查看>>