牛客国庆集训派对Day5 H - 我不爱她 Hash KMP

链接:https://www.nowcoder.com/acm/contest/205/H
来源:牛客网
 

题目描述

终于活成了自己讨厌的样子。

天空仍灿烂,它爱着大海。

你喜欢大海,我爱过你。

世界上充满了巧合。我们把每句话当成一个字符串,我们定义a对b的巧合值为a的最长后缀的长度并且它是恰好是b的前缀,这里的后缀或者前缀包括字符串的本身。
比如字符串“天空仍灿烂她喜欢大海”对“她喜欢大海我不爱她了我爱的只是与她初见时蔚蓝的天空”的巧合值为5,而字符串“她喜欢大海我不爱她了我爱的只是与她初见时蔚蓝的天空”对“天空仍灿烂她喜欢大海”的巧合值为2。
现在给出n个字符串由"ab"构成的字符串s1,s2,...,sn,求出对于所有1≤ i,j≤ n,si对sj的巧合值的和。

输入描述:

第一行一个整数T(T≤ 1000),表示数据组数。
每组数据第一行一个正整数n(1≤ n≤ 1e5)。接下来n行每行一个字符串si,保证字符串由"ab"构成。
保证单组数据有,保证所有数据有。

输出描述:

对于每组数据,输出一个整数,表示答案。

题解:

把所有前缀的hash存在map里面,只要对于每个后缀找到能和他匹配的个数即可。

然后会出现重复的问题,因为只是匹配最大的后缀和前缀。

重复的情况是某一个后缀完全包含另一个后缀,我们只要把串反向后用Kmp求出循环节,就知道当前下标为i的后缀包含了哪个后缀。

那么我们算答案的时候对于当前后缀 只要加上 出现次数 * (长度 -  循环节长度)即可。

卡了一下模1e9 + 7,改成ull自然溢出就过了。

代码:

#include <bits/stdc++.h>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

const int MOD = 1e9 + 7;
const double PI = acos (-1.);
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3f;
const int MAXN = 2e6 + 5;


char s[MAXN];
char *p[MAXN];
int len[MAXN];
#define seed 233
unordered_map<ull, int> q;

ull kpw[MAXN];
ull hs[MAXN];
int f[MAXN];
void getFail(char *P, int m) {
    f[0] = f[1] = 0;
    for (int i = 1; i < m; i++) {
        int j = f[i];
        while (j && P[i] != P[j])
            j = f[j];
        f[i + 1] = (P[i] == P[j]) ? j + 1 : 0;
    }
}

ll getHash(int l, int r) {
    return hs[r] - hs[l - 1] * kpw[r - l + 1];
}

char t[MAXN];

ll query(char * s, int n) {
    memcpy(t, s, n);
    reverse(t, t + n);
    getFail(t, n);
    for (int i = 0; s[i]; i++) hs[i + 1] = hs[i] * seed + s[i] - 'a' + 1;
    ll ret = 0;
    for (int i = 0; i < n; i++) {
        ll val = getHash(i + 1, n);
        int num = q[val];
        ret += (ll) num * (n - i - f[n - i]);
    }

    return ret;
}

void init() {
    kpw[0] = 1;
    for (int i = 1; i < MAXN; i++) kpw[i] = kpw[i - 1] * seed;
}

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    int T;
    cin >> T;
    init();
    while(T--) {
        int n;
        q.clear();
        scanf("%d", &n);
        int tot = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%s", s + tot);
            p[i] = s + tot;
            tot += (len[i] = strlen(s + tot)) + 1;
            for (int j = 0; j < len[i]; j++) {
                hs[j + 1] = hs[j] * seed + (*(p[i]+j) - 'a') + 1;
                q[hs[j + 1]]++;
            }
        }
        ll ans = 0;
        for (int i = 1; i <= n; i++) {
            ans += query(p[i], len[i]);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c6376315qqso/article/details/82950591