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)

posted @ 2019-01-23 15:58  Jathon-cnblogs  阅读(236)  评论(0编辑  收藏  举报