Birthday Cake
https://codeforces.com/gym/103118/problem/F
题目大意:给出 n n n 个字符串,问有多少对无序 ( i , j ) (i,j) (i,j) 满足:将第 i i i 个字符串与第 j j j 个首尾或尾首拼接,可以使得,从新串中间劈一刀,可以得到两个相等的串(不允许翻转)。
我们发现,所有可以被统计进入答案的 p a i r pair pair 可以分成两类,第一,两个字符串长度相等,第二,两个字符串长度不等。相等的情况好处理,字符串哈希,然后用 m a p map map 统计一下就行。主要就是长度不等的情况,这时候我们考虑枚举每个字符串 i i i ,因为答案要统计的是无序对,那么要么我们要么统计其作为小串的所有答案,要么就统计其作为大串的所有答案。
对于长度不相等的两个串,要满足条件肯定长下图这样( S S S 串为短串, T T T 为长串)。假如我们将遍历到的字符串视其为短串,那么我们发现我们很难快速找到符合条件的长串。于是我们可以视其为长串寻找短串,那么我们只要遍历前后缀每个相等的部分,用 h a s h + m a p hash+map hash+map 查找下图中 1 1 1 部分字符串的个数,即是符合条件的短串个数,即是将遍历到的字符串视为长串得到的所有合理答案。那么利用此方法我们就可以统计所有两串长度不等的答案。
注:这题最好运用双模数才不容易被卡。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4e5 + 10;
const ll mod1 = 50331653, mod2 = 998244353, base = 233;
int n;
string s[N];
vector<ll> h1[N], h2[N];
map< pair<ll, ll>, int> cnt;
ll b1[N], b2[N];
pair<ll, ll> ghs(int i, int l, int r) {
if (l > r) return {0, 0};
ll num1 = (h1[i][r] - h1[i][l - 1] * b1[r - l + 1] % mod1 + mod1) % mod1;
ll num2 = (h2[i][r] - h2[i][l - 1] * b2[r - l + 1] % mod2 + mod2) % mod2;
return {num1, num2};
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
b1[0] = b2[0] = 1;
for (int i = 1; i <= 400000; ++i) {
b1[i] = b1[i - 1] * base % mod1;
b2[i] = b2[i - 1] * base % mod2;
}
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
cin >> s[i];
}
for (int i = 1; i <= n; ++i) {
int L = s[i].length();
h1[i].emplace_back(0), h2[i].emplace_back(0);
for (int j = 0; j < L; ++j) {
h1[i].emplace_back( (h1[i][j] * base % mod1 + s[i][j]) % mod1 );
h2[i].emplace_back( (h2[i][j] * base % mod2 + s[i][j]) % mod2 );
}
cnt[{h1[i][L], h2[i][L]}]++;
}
long long ans = 0, t = 0;
for (int i = 1; i <= n; ++i) {
int L = s[i].length();
t += cnt[{h1[i][L], h2[i][L]}] - 1;
for (int len = 1; ; len++) {
int q = L - len + 1;
if (q - len < 2) break;
if (ghs(i, 1, len) == ghs(i, q, L)) {
ans += cnt[ghs(i, len + 1, q - 1)];
}
}
}
printf("%lld", ans + t / 2);
}