2020牛客暑期多校训练营(第二场)All with Pairs

题目链接

https://ac.nowcoder.com/acm/contest/5667/A

题目大意

定义 f(s , t) 为 s 的前缀和 t 的后缀的最大匹配,求$\sum ^{n}_{i=1}\sum ^{n}_{j=1}f\left( s_{i},s_{j}\right) ^{2}$

解题思路

一个很显然的做法:

先对字符串进行 hash , 然后用 cnt 统计每个字符串的所有后缀的个数 , 再遍历每个字符串的所有前缀统计个数即可

然而我们会发现上述做法会有重复统计的情况 , 如 f ("abab" , "aba")

"abab" 和 "aba" 最大匹配前后缀很显然是 "aba",但如若我们用上述方法直接统计就会算上 cnt["a"]

而 "a" 为 "aba" 和 "aba" 的最大前后缀匹配(不包含本身) , 也就是 "aba" 在失配情况下得到的次大匹配

这不正是 kmp 的 next 数组吗 ? 

于是我们只要在每次统计前缀 S 时减去这个前缀对应 next 数组的值即可(ans += cnt[S] - cnt[nex[ S ]])

AC_Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ull unsigned long long
const int base = 233;
const int N = 1e6 + 10 , mod = 998244353;
ull power[N] , suf[N];
string s[N];
unordered_map<ull , int>cnt;
int n , ans , nex[N] , now[N];
void get_nex(string s , int *nex)
{
    int i = 0 , j = -1 , len = s.size();
    nex[i] = j;
    while(i < len)
    {
        while(j != -1 && s[i] != s[j]) j = nex[j];
        nex[++ i] = ++ j ;
    }
}
void Hash(string s)
{
    ull suf = 0 , p = 1;
    for(int i = s.size() - 1 ; i >= 0 ; i --)
    {
        suf += p * (s[i] - 'a' + 1);
        p *= base;
        cnt[suf] ++ ;
    }
}
long long get(int x)
{
    return x * x % mod;
}
signed main()
{
    ios::sync_with_stdio(false) , cin.tie(0) , cout.tie(0);
    cin >> n; 
    for(int i = 1 ; i <= n ; i ++)
    {
        cin >> s[i];
        Hash(s[i]);
    }
    for(int i = 1 ; i <= n ; i ++)
    {
        ull pre = 0 , p = 233;
        int len = s[i].size();
        get_nex(s[i] , nex);
        for(int j = 0 ; j < len ; j ++)
        {
            pre = pre * p + (s[i][j] - 'a' + 1) ;        
            now[j] = cnt[pre];
        }
        for(int j = 0 ; j < len ; j ++) now[nex[j + 1] - 1] -= now[j];
        for(int j = 0 ; j < len ; j ++) ans += now[j] * get(j + 1) , ans %= mod;
    }
    cout << ans << '\n';
    return 0;
}
上一篇:Java多线程(三)显式锁和AQS


下一篇:对死锁的理解