KMP算法
相关题目
题目描述
给定一个字符串 $S$,以及一个模式串 $P$,所有字符串中只包含大小写英文字母以及阿拉伯数字。
模式串 $P$ 在字符串 $S$中多次作为子串出现。
求出模式串 $P$ 在字符串 $S$ 中所有出现的位置的起始下标。
输入格式
第一行输入整数 $N$,表示字符串 $P$ 的长度。
第二行输入字符串 $P$。
第三行输入整数 $M$,表示字符串 $S$ 的长度。
第四行输入字符串 $S$。
输出格式
共一行,输出所有出现位置的起始下标(下标从 00 开始计数),整数之间用空格隔开。
数据范围
$1 \le N \le 10^5$
$1 \le M \le 10^6$
输入样例:
输出样例:
AC代码
第一种方式
$p$和$s$数组的下标存储都是从$1$开始,每次匹配时候与$j + 1$位置进行比较,算法更易理解和记忆,推荐第一种方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> using namespace std;
const int N = 100010,M = 1000010; char p[N],s[M]; int ne[M]; int n, m;
int main(){ cin >> n >> p+1 >> m >> s+1; for(int i = 2,j = 0;i<=n;i++){ while(j && p[i] != p[j+1]) j = ne[j]; if(p[i] == p[j+1]) j++; ne[i] = j; } for(int i = 1,j = 0;i<=m;i++){ while(j && s[i] != p[j + 1]) j = ne[j]; if(s[i] == p[j + 1]) j++; if(j == n){ cout << i - j << ' '; j = ne[j]; } } }
|
第二种方式
此种方式的$p$和$s$数组的下标存储都是从$0$开始,相较第一种方式更难理解,$ne[i]$表示最长前后缀匹配的前缀最后一个字母的下一个字母,这种方式理解更复杂,不推荐,但在特定问题下可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <cstring> #include <iostream> #include <algorithm>
using namespace std; const int N = 1000010; int n, m; char s[N], p[N]; int ne[N];
int main() { cin >> m >> p >> n >> s; for(int i = 1,j = 0; i < m ; i ++) { while(j && p[i] != p[j]) j = ne[j - 1]; if(p[i] == p[j]) j++; ne[i] = j; } for(int i = 0,j = 0; i < n; i ++) { while(j && s[i] != p[j]) j = ne[j - 1]; if(s[i] == p[j]) j++; if(j == m) { cout << i + 1 - j << " "; } } return 0; }
|