1. 程式人生 > >牛客國慶集訓派對Day5 H-我不愛她 (KMP+字串雜湊)

牛客國慶集訓派對Day5 H-我不愛她 (KMP+字串雜湊)

題目描述

終於活成了自己討厭的樣子。  

天空仍燦爛,它愛著大海。

你喜歡大海,我愛過你。

世界上充滿了巧合。我們把每句話當成一個字串,我們定義a對b的巧合值為a的最長字尾的長度並且它是恰好是b的字首,這裡的字尾或者字首包括字串的本身。 比如字串“天空仍燦爛她喜歡大海”對“她喜歡大海我不愛她了我愛的只是與她初見時蔚藍的天空”的巧合值為5,而字串“她喜歡大海我不愛她了我愛的只是與她初見時蔚藍的天空”對“天空仍燦爛她喜歡大海”的巧合值為2。 現在給出n個字串由"ab"構成的字串s1,s2,...,sn,求出對於所有1≤ i,j≤ n,si對sj的巧合值的和。

輸入描述:

第一行一個整數T(T≤ 1000),表示資料組數。
每組資料第一行一個正整數n(1≤ n≤ 105)。接下來n行每行一個字串si,保證字串由"ab"構成。
保證單組資料有,保證所有資料有。

輸出描述:

對於每組資料,輸出一個整數,表示答案。

示例1

輸入

複製

1
2
abb
bab

輸出

複製

9

解題思路:很容易想到雜湊,對每一個字串的所有字首雜湊,用一個map記錄雜湊值出現的(次數*字串長度)

然後再列舉所有字串的字尾,求雜湊值,然後更新答案 ans+=mp[hash];

這麼想貌似可做,但是可能會出現重複計算的情況如單個字串

ababab

我們在列舉字尾的時候,會把 ab,abab也算進了答案裡面,但是題目只用求最長的。因此我們要計算出每個字首對答案新增的貢獻是多少。考慮,ab,abab,ababab,ab對答案新增了2,abab對答案也新增了2,ababab對答案也貢獻了2.加起來就是6,那麼這個貢獻怎麼算呢?不難看出,就是字串長度減去該字串的字尾的最長公共字首。就是len-next[len]。

#include <iostream>
#include <vector>
#include <string.h>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int MAXN = 1500005;
unordered_map<ull,ll> mp;
ull P1 = 131;
ull g[MAXN];
ull Hash[MAXN];
ull getHash(int x, int len)
{
    return Hash[x + len - 1] - Hash[x - 1] * g[len];
}
void hhash(char *s, int len)
{
    for (int i = 1; i <= len; i++)
        Hash[i] = (Hash[i - 1] * P1 + s[i - 1]);
}

void kmp(char *s,int len,int *nxt){
    int i,j;
    j=nxt[0]=-1;
    i=0;
    while(i<len){
        while(j!=-1&&s[i]!=s[j])
            j=nxt[j];
        nxt[++i]=++j;
    }
}

char s[MAXN];
int nxt[MAXN];
int len[MAXN];
int slen[MAXN];
ll ans=0;
int main()
{
    g[0] = 1;
    for (int i = 1; i <= MAXN - 1; i++)
        g[i] = (g[i - 1]) * P1;
    int T;
    scanf("%d", &T);
    while (T--)
    {
        mp.clear();
        ans=0;
        int N;
        scanf("%d",&N);
        for(int i=0;i<N;i++){
            scanf("%s",s+slen[i]);
            len[i]=strlen(s+slen[i]);
            slen[i+1]=slen[i]+len[i];
        }

        hhash(s,slen[N]);

        for(int i=0;i<N;i++){
            kmp(s+slen[i],len[i],nxt);
            for(int j=1;j<=len[i];j++)
                mp[getHash(1+slen[i],j)]+=j-nxt[j];
        }

        for(int i=0;i<N;i++)
            for(int j=1;j<=len[i];j++)
                ans+=mp[getHash(1+slen[i]+j-1,len[i]-j+1)];
        printf("%lld\n",ans);

    }

    return 0;
}