字符串系列——KMP模板整理
KMP模板整理
KMP与扩展KMP;
/*vs 2017/ vs code以外编译器,去掉windows.h头文件和system("pause");*/ #include<iostream> #include<cstdio> #include<cstring> #include<Windows.h> #include<cmath> #include<algorithm> using namespace std; const int maxn = 100010; /* KMP */ void Get_Next(char x[], int m, int Next[]) { /*x[]为模式串P, m为模式串P的长度*/ int i = 0, j; j = Next[0] = -1; /*以P[0]为终点的子串没有真前缀,有且只有一个假前缀为本身,故Next[0]= -1*/ while (i < m) { while (-1 != j && x[i] != x[j]) j = Next[j]; /*while当P[i]与P[j]不匹配时, 去寻找上一个与P[j]处境相同的位置,即有相同前缀的点Next[j],与P[i]比较*/ Next[++i] = ++j; /*当P[i]与P[j]处境相似且元素相等时, 那么Next[i+ 1]= j+ 1; 意思是P[i+ 1]与P[j+ 1]的处境相似,即有相同的前缀*/ } } int KMP_Count(char x[], int m, char y[], int n, int Next[]) { /*x[]为模式串P,m为P的长度;y[]为文本串T,n为T的长度*/ int i, j; i = j = 0; int cnt = 0; while (i < n) { while (-1 != j && y[i] != x[j]) j = Next[j]; /*若T[i]!= P[j];则寻找上一个与P[j]前缀相同的地址Next[j];然后与T[i]比较*/ ++i; ++j; if (j >= m) { ++cnt; j = Next[j]; } } return cnt; } /* 扩展KMP */ void Get_e_Next(char x[], int m, int e_Next[]) { /*x[]为模式串P;m是P的长度;e_Next[i]表示模式串P中, 以P[i]为起点的后缀与模式串P的最长公共前缀的长度*/ e_Next[0] = m; /*字符串s0与自身的最长前缀长度等于自身的长度*/ int j = 0; while (j + 1 < m&& x[j] == x[j + 1]) j++; e_Next[1] = j; /*用while循环寻找以P[1]为起点的后缀与P的最长公共前缀*/ int k = 1; for (int i = 2; i < m; i++) { /*此时,以P[k]为起点的后缀是最新(近)一次循环查找过与模式串P最长公共前缀的子串*/ int p = k+ e_Next[k] - 1; /*;P[p]是模式串P以P[k]为起点的后缀与P的最大公共前缀的末位元素; 即:P[k, p]= P[0, p- k]; 最大公共前缀的长度为(p- k+ 1)*/ int L = e_Next[i - k]; /*由上面P[k, p]= P[0, p- k]可得,故P[i, p]= P[i- k, p- k]; L= e_Next[i- k]; 即以P[i- k]为起点的后缀有长度为L的前缀与P[0, L- 1]相同, 即由上面的关系可证,P[i]为起点的后缀也与P有一个长度为L的最大公共前缀*/ if (i + L <= p) { /*如果i+ L的长度不超过目前已扫描的范围,则e_Next[i]= L; 否则,则要重新扩大处理的范围*/ e_Next[i] = L; } else { /*若上面判断目前扫描的范围已经不足时,继承之前处理的结果,继续向后处理*/ j = max(0, p - i + 1); /*j为p- i+ 1,即P[i]之后已经处理过的元素数量;P[i+ j]是其后第一个未处理的元素*/ while (i + j < m&& x[i + j] == x[j]) j++; /*从P[i+ j]开始,比较P[i]为起点的后缀与P,在两者第j位以后的部分开始比较*/ e_Next[i] = j; k = i; /*处理完成后,k更新为i;i+ e_Next[i]- 1是当前处理过的最后一个元素; 当以后的处理要超过这一界线时,要再次处理,然后更新k的值*/ } } } void e_KMP(char x[], int m, char y[], int n, int e_Next[], int extend[]) { /*x[]是模式串P,m是P的长度,y[]是文本串T,n是T的长度*/ int j = 0; while (j < n&& j < m&& x[j] == x[j]) j++; extend[0] = j; /*用while循环寻找以T[0]为起点的后缀与P的最长公共前缀*/ int k = 0; for (int i = 1; i < n; i++) { int p = extend[k] + k - 1; int L = e_Next[i - k]; if (i + L < p + 1) { extend[i] = L; } else { j = max(0, p - i + 1); while (i + j < n&& j < m&& y[i + j] == x[j]) j++; extend[i] = j; k = i; } } } char ss_0[maxn]; char ss_1[maxn]; int Next[maxn]; int e_Next[maxn]; int extend[maxn]; int KMP(); int E_KMP(); int main() { int menu; printf("\n\tKMP,选择0\n\te-KMP,选择1\n\t请选择:"); scanf_s("%d", &menu); if (menu == 0) { int x= KMP(); if (x< 0) printf("\n\t错误\n"); } else if (menu == 1) { int x= E_KMP(); if (x< 0) printf("\n\t错误\n"); } else { printf("\n\t错误\n"); } system("pause"); return 0; } int KMP() { /*可直接修改函数名,为KMP的主函数*/ system("cls"); cin >> ss_0; cin >> ss_1; int len_0 = strlen(ss_0); int len_1 = strlen(ss_1); Get_Next(ss_0, len_0, Next); int tmp= KMP_Count(ss_0, len_0, ss_1, len_1, Next); printf("%d\n", tmp); return 0; } int E_KMP() { /*可直接修改函数名,为扩展KMP的主函数*/ system("cls"); cin >> ss_0; cin >> ss_1; int len_0 = strlen(ss_0); int len_1 = strlen(ss_1); Get_e_Next(ss_0, len_0, e_Next); e_KMP(ss_0, len_0, ss_1, len_1, e_Next, extend); for (int i = 0; i < len_1; i++) { printf("%d%c", extend[i], i == len_1 - 1 ? '\n' : ' '); } system("pause"); return 0; }
/*vs 2017/ vs code以外编译器,去掉windows.h头文件和system("pause");*/ #include<iostream> #include<cstdio> #include<cstring> #include<Windows.h> #include<cmath> #include<algorithm> using namespace std; const int maxn = 100010; /* KMP */ void Get_Next(char x[], int m, int Next[]) { /*x[]为模式串P, m为模式串P的长度*/ int i = 0, j; j = Next[0] = -1; while (i < m) { while (-1 != j && x[i] != x[j]) j = Next[j]; Next[++i] = ++j; } } int KMP_Count(char x[], int m, char y[], int n, int Next[]) { /*x[]为模式串P,m为P的长度;y[]为文本串T,n为T的长度*/ int i, j; i = j = 0; int cnt = 0; while (i < n) { while (-1 != j && y[i] != x[j]) j = Next[j]; ++i; ++j; if (j >= m) { ++cnt; j = Next[j]; } } return cnt; } /* 扩展KMP */ void Get_e_Next(char x[], int m, int e_Next[]) { /*x[]为模式串P;m是P的长度;e_Next[i]表示模式串P中, 以P[i]为起点的后缀与模式串P的最长公共前缀的长度*/ e_Next[0] = m; int j = 0; while (j + 1 < m&& x[j] == x[j + 1]) j++; e_Next[1] = j; int k = 1; for (int i = 2; i < m; i++) { int p = k+ e_Next[k] - 1; int L = e_Next[i - k]; if (i + L <= p) { e_Next[i] = L; } else { j = max(0, p - i + 1); while (i + j < m&& x[i + j] == x[j]) j++; e_Next[i] = j; k = i; } } } void e_KMP(char x[], int m, char y[], int n, int e_Next[], int extend[]) { /*x[]是模式串P,m是P的长度,y[]是文本串T,n是T的长度*/ int j = 0; while (j < n&& j < m&& x[j] == x[j]) j++; extend[0] = j; int k = 0; for (int i = 1; i < n; i++) { int p = extend[k] + k - 1; int L = e_Next[i - k]; if (i + L < p + 1) { extend[i] = L; } else { j = max(0, p - i + 1); while (i + j < n&& j < m&& y[i + j] == x[j]) j++; extend[i] = j; k = i; } } } char ss_0[maxn]; char ss_1[maxn]; int Next[maxn]; int e_Next[maxn]; int extend[maxn]; int KMP(); int E_KMP(); int main() { int menu; printf("\n\tKMP,选择0\n\te-KMP,选择1\n\t请选择:"); scanf_s("%d", &menu); if (menu == 0) { int x= KMP(); if (x< 0) printf("\n\t错误\n"); } else if (menu == 1) { int x= E_KMP(); if (x< 0) printf("\n\t错误\n"); } else { printf("\n\t错误\n"); } system("pause"); return 0; } int KMP() { /*可直接修改函数名,为KMP的主函数*/ system("cls"); cin >> ss_0; cin >> ss_1; int len_0 = strlen(ss_0); int len_1 = strlen(ss_1); Get_Next(ss_0, len_0, Next); int tmp= KMP_Count(ss_0, len_0, ss_1, len_1, Next); printf("%d\n", tmp); return 0; } int E_KMP() { /*可直接修改函数名,为扩展KMP的主函数*/ system("cls"); cin >> ss_0; cin >> ss_1; int len_0 = strlen(ss_0); int len_1 = strlen(ss_1); Get_e_Next(ss_0, len_0, e_Next); e_KMP(ss_0, len_0, ss_1, len_1, e_Next, extend); for (int i = 0; i < len_1; i++) { printf("%d%c", extend[i], i == len_1 - 1 ? '\n' : ' '); } system("pause"); return 0; }
End;