[KMP算法]字符串匹配
适用问题举例:
给定两个字符串,问:模式串是否在主串中出现过。 如果出现过,输出模式串在主串中首次出现的位置。 如果没出现过,输出-1。
暴力算法:时间复杂度为O(m*n)
1、先将模式串的第一个字符与主串的第一个字符对齐。
2、从对齐位置开始对每个字符进行逐一匹配。
3、若单个字符匹配成功,则继续比对。
4、若单个字符匹配失败,则把模式串向右移动一个单位。
int find(char p[], char t[]) { int m = strlen(p);//strlen用于获得char数组的长度 int n = strlen(t);//m为模式串长度,n为主串长度 int j = 0, i = 0;//i是主串中此时在比对的位置,j是模式串的位置 while (i < n && j < m) { if (t[i] == p[j]) i++, j++;//比对成功,进行下个字符的比对 else { i -= j - 1; j = 0; } if (j == m)return i - j;//比对成功 } return -1;//比对失败返回-1 }
KMP算法解法:
先给代码,一团雾水然后往下看hh。
void init(char p[]) { Next[0] = -1; int i = 0, j = -1; int m = strlen(p); while (i < m) { if (j == -1 || p[i] == p[j]) Next[++i] = ++j; else j = Next[j]; } } int find(char p[], char t[]) { int m = strlen(p),n = strlen(t),j = 0, i = 0; while (i < n && j < m)//逐位比对 { if (j == -1 || t[i] == p[j])//比对成功 i++,j++; else j = Next[j];//i不变,j回退 if (j == m) return i - j;//匹配成功 } return -1; }
构建Next数组:
不难发现,我们在构建计算Next数组时只用到了模式串而不需要主串的数组。
对于每个模式串都有确定的Next数组。例如:
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
模式串 | A | B | C | D | A | B | C | E | F | G |
Next[] | -1 | 0 | 0 | 0 | 0 | 1 | 2 | 3 | 0 | 0 |
他的目标是实现快速比对,可以有效减少匹配的次数。
当然Next表求出来不止适用于KMP算法,遇到很多字符串匹配查找的问题都可以想一想用这个
简短代码:(时间复杂度O(m+n))
int n = strlen(t + 1), m = strlen(p + 1); //先求出匹配串的next数组 for (int i = 2, j = 0;i <= m;i++) { while (j && p[j + 1] != p[i])j = Next[j]; if (p[j + 1] == p[i])j++; Next[i] = j; } //再用得到的next 去匹配s for (int i = 1, j = 0;i <= n;i++) { while (j && p[j + 1] != t[i])j = Next[j]; if (p[j + 1] == t[i])j++; if (j == m) {//匹配成功 cout << i - m + 1 << endl; j = Next[j]; } }
简短版的代码是CST的一个学长写的,看不明白的不要勉强自己,看懂上面那个就行了,KMP本身就不是一个好理解的算法orz
制作:BDT20040