HDU-1711-Number Sequence(KMP)(Rabin-Karp)
- Rabin-Karp
Accepted 1711 904MS 5272K 1310 B G++ #include "bits/stdc++.h" using namespace std; typedef long long LL; const int MAXN = 1e6 + 5; const int SEED = 1e9 + 7; int arr[MAXN]; int main() { int t, n, m, k; scanf("%d", &t); while (t--) { LL p = 0, s = 0, head = 1; bool flag = false; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &arr[i]); } // 获取模式串(M数组)的哈希值 for (int i = 1; i <= m; i++) { scanf("%d", &k); head *= SEED; p = p * SEED + k; } // 获取arr[0]所表示的长度为m的串的哈希值 for (int i = 1; i < m; i++) { /* 比较标准的写法是s = (s * SEED + arr[i]) % MOD;(MOD是一个和SEED互质的数) 这里利用LL的溢出来省略MOD,可以将MOD看成2的64次方 */ s = s * SEED + arr[i]; } for (int i = 1; i <= n - m + 1; i++) { // 用arr[i - 1]所表示的长度为m的串的哈希值得到arr[i]所表示的长度为m的串的哈希值 s = s * SEED - head * arr[i - 1] + arr[i + m - 1]; if (s == p) { flag = true; printf("%d\n", i); break; } } if (!flag) { puts("-1"); } } return 0; }
Rabin-Karp获取哈希值的形式有点像进制转换,由于计算机内二进制加减是不管符号的所以p和s变成负数也无所谓,用unsigned long long和long long是一样的。但是Rabin-Karp不保证匹配结果绝对正确,因为不同的串哈希值可能一样(long long的范围只有2的64次方,但是本题数组值的范围是[-1000000, 1000000],只要三四位产生的串的数量long long就不够放了)所以如果竞赛中用此法错了,可以尝试改SEED。还不行那就只能换方法了。
- KMP
Accepted 1711 842MS 5360K 802 B G++ #include "bits/stdc++.h" using namespace std; const int MAXN = 1e6 + 5; const int MAXM = 1e4 + 5; int s[MAXN], p[MAXM], Next[MAXM] = {-1}; int t, n, m; int kmp() { int i = 0, j = -1; // 求模式串的Next while (i < m) { if (j == -1 || p[i] == p[j]) { Next[++i] = ++j; } else { j = Next[j]; } } i = 0, j = 0; while (i < n) { if (j == -1 || s[i] == p[j]) { i++; if (++j == m) { // 由于题目题目描述的数组下标从1开始,所以追加1 return i - m + 1; } } else { j = Next[j]; } } return -1; } int main() { scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) { scanf("%d", &s[i]); } for (int i = 0; i < m; i++) { scanf("%d", &p[i]); } printf("%d\n", kmp()); } return 0; }
复杂度为O(M + N)