1. 程式人生 > >牛客國慶集訓派對Day5 H

牛客國慶集訓派對Day5 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;
}