题目链接
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; }