1. 程式人生 > >牛客國慶集訓派對Day5 H題 我不愛她

牛客國慶集訓派對Day5 H題 我不愛她

(有任何問題歡迎留言或私聊 && 歡迎交流討論哦

Catalog

Problem:傳送門

 原題目描述在最下面。

Solution:

先放一個群裡某大佬的解釋:

就是一個串w是a的字首也是b的字尾 那麼border(w)也是a的字首也是b的字尾 (因為border(w)是w的字首也是w的字尾) 所以把每個w的貢獻記為len(w) - len(border(w)) 如果w被匹配了 說明border(w)一定不是最長的 相當於加上len(w)的時候 把不是最長的len(border(w))貢獻去掉 最後累加起來剩下的都是最長的

在告訴你一個小祕密:

int ans =
0, t = 0; for(int i = len; i > 0; ) { ans += i - nex[i]; i = nex[i]; p[t++] = nex[i]; } printf("ans = %d\n", ans);///ans 的值和這個字串的長度一樣哦!!! ///只有p數組裡存的字首才是這個字串的字尾!(如果這句話是錯的,望大佬指出!

 講講我的理解吧

 如那位大佬所言,若字串ww是所求的巧合值,也就是wwaa的字尾,也是bb的字尾,同樣border(w)border(w)也滿足此條件,只不過len(border(w))<

len(w)len(border(w))<len(w)

 問題在於,你對每兩個字串求borderborder時間上肯定tletle了。怎麼解決呢?

 題解上講的是每個字首/字尾的貢獻記為len(w)len(border(w))len(w)-len(border(w))

 然後你把所有的字首子串貢獻記為len(i)len(border(i))len(i)-len(border(i)),再用mapmap存一下每個字首oror

字尾的出現次數。最後遍歷一遍所有後綴或字首,累加貢獻即可。

 我上面寫了只有pp數組裡的字首才會是這個字串的字尾!而且每個字串不會有算重複的地方,pp陣列求一遍kmpkmpfailfail陣列就行了。

AC_Code:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <unordered_map>
#define fi first
#define se second
#define iis std::ios::sync_with_stdio(false)
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<LL, LL> pii;
/*
就是一個串w是a的字首也是b的字尾
那麼border(w)也是a的字首也是b的字尾
(因為border(w)是w的字首也是w的字尾)
所以把每個w的貢獻記為len(w) - len(border(w))
如果w被匹配了 說明border(w)一定不是最長的 相當於加上len(w)的時候 把不是最長的len(border(w))貢獻去掉
最後累加起來剩下的都是最長的
int ans = 0, t = 0;
for(int i = len; i > 0; ) {
    ans += i - nex[i];
    i = nex[i];
    p[t++] = nex[i];
}
printf("ans = %d\n", ans);///ans 的值和這個字串的長度一樣哦!!!
///只有p數組裡存的字首才是這個字串的字尾!(如果這句話是錯的,望大佬指出!
*/
const long long HMOD[] = {2078526727, 2117566807};
const long long BASE[] = {1572872831, 1971536491};
const int MXN = 1e5 + 5;
const int MXT = 1e6 + 5e5 + 6;

char s[MXT];
int fail[MXT];
std::vector<pii> ve;
unordered_map<LL, int> mp;
void get_fail(char *t,int m) {
    fail[0] = -1;
    for(int i = 0,k = -1; i < m;) {
        if(k == -1 || t[i] == t[k]) {
            ++ i; ++ k;
            fail[i] = k;
        }else k = fail[k];
    }
}
int main(int argc, char const *argv[]) {
    LL N = (int)2e9, hh;
    int tim;
    scanf("%d", &tim);
    while(tim--) {
        ve.clear();
        mp.clear();
        int n; scanf("%d", &n);
        for(int i = 0; i < n; ++i) {
            scanf("%s", s);
            int len = strlen(s);
            get_fail(s, len);
            pii tmp = {0, 0};
            for(int i = 0; i < len; ++i) {
                tmp.fi = (tmp.fi*BASE[0] + (s[i]-'a'+1))%HMOD[0];
                tmp.se = (tmp.se*BASE[1] + (s[i]-'a'+1))%HMOD[1];
                hh = tmp.fi*N + tmp.se;
                ve.push_back({hh, i + 1 - fail[i + 1]});
            }
            tmp = {0, 0};
            LL p1 = 1, p2 = 1;
            for(int i = len - 1; i >= 0; --i) {
                tmp.fi = (tmp.fi + (s[i]-'a'+1)*p1)%HMOD[0];
                tmp.se = (tmp.se + (s[i]-'a'+1)*p2)%HMOD[1];
                hh = tmp.fi*N + tmp.se;
                mp[hh] ++;
                p1 = p1 * BASE[0] % HMOD[0];
                p2 = p2 * BASE[1] % HMOD[1];
            }
        }
        int len = ve.size();
        LL ans = 0;
        for(int i = 0; i < len; ++i) {
            ans += mp[ve[i].first]*ve[i].se;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

Problem Description:

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

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

你喜歡大海,我愛過你。

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

e