POI2006 PAL-Palindromes
给出 \(n\) 个回文串 \(s_1\sim s_n\),将它们两两组合拼接得到 \(n^2\) 个新串。求这些新串中有多少回文串。
\(\sum\limits_{i=1}^n|s_i|\le 2\times 10^6\)。
约定:
-
记字符串 \(s\) 的反串为 \(s^R\),比如 \((\texttt{ab})^R=\texttt{ba}\)。
-
记 \(pre(s,l)\) 为字符串 \(s\) 长度为 \(l\) 的前缀,\(suf(s,l)\) 为字符串 \(s\) 长度为 \(l\) 的后缀。
其实 \(s_1\sim s_n\) 不是回文串也可以做的,所以不管这个性质。
考虑哈希,定义字符串 \(s\) 的哈希函数 \(H(s)=\sum\limits_{i=1}^{|s|}(P^{|s|-i}\times \text{ASCII}(s_i))\),其中取 \(P=1145141\),函数的返回值类型为 unsigned long long,在计算过程中自然溢出,默认不会冲突。
用哈希求解回文串题目先预处理出正、反串的前缀哈希值。开两个桶 \(mp_{0/1,x}\) 表示正/反串哈希值为 \(x\) 的 \(s\) 数量。然后我们将新串分为两类:
-
拼接的两个字符串长度相同
枚举位于前面的那个串 \(s_i\),显然贡献为 \(\sum\limits_{j=1}^n[s_j=s_i^R]\),即 \(mp_{1,s_i}\)。
-
拼接的两个字符串长度不同
枚举较长的那个串 \(s_i\),用它去和较短的串拼接。又分为两类:
-
\(s_i\) 在前
这种情况一定是 \(s_i\) 的一个非空真后缀回文,且该非空真后缀之前的非空真前缀与短串对称。枚举回文非空真后缀的位置 \(j\),用哈希判断,贡献为 \(mp_{1,H(pre(s_i,j-1))}\)。
-
\(s_i\) 在后
这种情况一定是 \(s_i\) 的一个非空真前缀回文,且该非空真前缀之后的非空真后缀与短串对称。枚举回文非空真前缀的位置 \(j\),用哈希判断,贡献为 \(mp_{0,H((suf(s_i,j+1))^R)}\)。
可以看到两种情况的讨论是类似的。有人可能会问 \(mp\) 是对全局维护的,上面用 \(mp\) 计算贡献会不会算上比 \(s_i\) 长的串。显然是不会的,因为上面使用 \(mp\) 时均访问了短串的哈希值的下标以统计个数,而长串的哈希值时不会和短串的哈希值冲突的。
-
使用 __gnu_pbds::gp_hash_table 维护桶,时间、空间复杂度均为 \(\mathcal{O}\left(\sum\limits_{i=1}^n |s_i|\right)\)。
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define ull unsigned long long
using namespace std; const int N = 2e6 + 5; const ull P = 1145141;
vector<ull> H[N][2]; int a[N], n; string s; ull ans, pw[N];// 0 正; 1 反
__gnu_pbds::gp_hash_table<ull, ull> mp[2];
ull Hash(int id, bool type, int l, int r) {
if (!type) return H[id][0][r] - H[id][0][l - 1] * pw[r - l + 1];
return H[id][1][l] - H[id][1][r + 1] * pw[r - l + 1];
}
signed main() {
cin.tie(0), cout.tie(0), ios::sync_with_stdio(0); cin >> n;
for (int i = pw[0] = 1; i < N; ++i) pw[i] = pw[i - 1] * P;
for (int i = 1; i <= n; ++i) {
cin >> a[i] >> s; s = ' ' + s;
H[i][0].resize(a[i] + 5), H[i][1].resize(a[i] + 5);
for (int j = 1; j <= a[i]; ++j) H[i][0][j] = H[i][0][j - 1] * P + s[j];
for (int j = a[i]; j >= 1; --j) H[i][1][j] = H[i][1][j + 1] * P + s[j];
++mp[0][H[i][0][a[i]]], ++mp[1][H[i][1][1]];
}
for (int i = 1; i <= n; ++i) {
ans += mp[1][H[i][0][a[i]]];
for (int j = 1; j < a[i]; ++j)
if (Hash(i, 0, 1, j) == Hash(i, 1, 1, j)) ans += mp[0][H[i][1][j + 1]];
for (int j = 2; j <= a[i]; ++j)
if (Hash(i, 0, j, a[i]) == Hash(i, 1, j, a[i]))
ans += mp[1][H[i][0][j - 1]];
}
return cout << ans, 0;
}

浙公网安备 33010602011771号