牛客國慶集訓派對Day5 H-我不愛她 (KMP+字串雜湊)
阿新 • • 發佈:2018-12-14
題目描述
終於活成了自己討厭的樣子。
天空仍燦爛,它愛著大海。
你喜歡大海,我愛過你。
世界上充滿了巧合。我們把每句話當成一個字串,我們定義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; }