Gym

Gym - 102470J Stamming Aliens 字符串哈希

题意

给出一个字符串,寻找最长的子串,使得这个子串的出现次数不少于(m) 次,且输出这个子串最后一次出现的位置。若有多个子串符合条件,则找出最右边的子串。

若没有,输出"none"

[m leq |s| leq 40000 ]

分析

此题显然具有二分性质,寻找子串也可以哈希做。对每个后缀哈希,二分长度,枚举所有长度为(len) 的子串,对哈希值排序,如果出现同样的(m) 次,(return quad true)

当然此题也可以SA做,GYM上的时限是1s,必须SA,HDU的原题是5s

代码

const ull P = 13331;
ull H[maxn], Pow[maxn];
ull Hash[maxn];
int rak[maxn];
char ss[maxn];

int n, m, pos;

bool cmp(int a, int b) {
    if (Hash[a] < Hash[b]) return true;
    if (a < b && Hash[a] == Hash[b]) return true;
    return false;
}

bool check(int len) {
    int c = -1;
    pos = -1;
    for (int i = 0; i <= n - len; i++) {
        rak[i] = i;
        Hash[i] = H[i] - H[i + len] * Pow[len];
    }
    sort(rak, rak + n - len + 1, cmp);
    for (int i = 0; i <= n - len; i++) {
        if (!i || Hash[rak[i]] != Hash[rak[i - 1]]) c = 0;
        if (++c >= m) pos = max(pos, rak[i]);
    }
    return pos >= 0;
}

int main() {
    Pow[0] = 1;
    for (int i = 1; i <= maxn - 3; i++) Pow[i] = Pow[i - 1] * P;
    while (scanf("%d", &m)) {
        if (!m) break;
        scanf("%s", ss);
        n = strlen(ss);
        H[n] = 0;
        for (int i = n - 1; i >= 0; i--) H[i] = H[i + 1] * P + (ull)(ss[i] - 'a');
        if (!check(1)) {
            puts("none");
            continue;
        }
        int l = 1, r = n;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (check(mid)) l = mid;
            else r = mid - 1;
        }
        check(l);
        printf("%d %d
", l, pos);
    }
}

原文地址:https://www.cnblogs.com/hznumqf/p/13546733.html