CF1080E Sonya and Matrix Beauty - 哈希 + manacher
Description
给定一个 \(n \times m\) 的字符矩阵,请求出有多少个子矩阵在重排子矩阵每一行的字符后,使得子矩阵的每行每列都是回文串。
Solution
如果一行能构成回文串,那么最多只能有一种字符出现奇数次。
如果一个矩阵的每一行和每一列都是回文串,那么除了满足上面的要求外,第\(i\)行和第\(n-i+1\)的每种字母出现的次数必须都相同。
所以我们可以枚举两列,然后对每一行的字母把出现次数\(hash\)起来,然后就是求由每一行的哈希值构成的序列的回文子串个数,\(manacher\)解决即可。
另外要注意的是如果某一行不能构成回文串,那么不应该计入到\(manacher\)的统计中去。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int _ = 500 + 10;
const int base = 233;
const ll mod = 20190816170251;
int N, M, cnt[_][_][30], p[_];
char s[_][_];
ll H[_][_], val[_][_], t[_], ans;
ll Hash(int *a) {
ll ret = 0;
for (int i = 1; i <= 26; ++i) ret = (ret * base % mod + a[i]) % mod;
return ret;
}
bool check(int i, int l, int r) {
ll x = val[i][r] ^ val[i][l - 1];
return (!x) || (!(x - (x & (-x))));
}
void manacher() {
memset(p, 0, sizeof(p));
for (int i = 1, r = 0, mid = 0; i <= N * 2; ++i) {
if (t[i] < 0) continue;
if (i < r) p[i] = min(r - i, p[mid * 2 - i]);
while (i - p[i] >= 0 && i + p[i] <= N * 2 + 1 && t[i - p[i]] == t[i + p[i]])
++p[i];
if (i + p[i] > r) r = i + p[i] - 1, mid = i;
// printf("%d\n", p[i]);
ans += p[i] / 2;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("matrix.in", "r", stdin);
freopen("matrix.out", "w", stdout);
#endif
scanf("%d%d", &N, &M);
for (int i = 1; i <= N; ++i) {
scanf("%s", s[i] + 1);
for (int j = 1; j <= M; ++j) {
for (int k = 1; k <= 26; ++k) cnt[i][j][k] = cnt[i][j - 1][k];
++cnt[i][j][s[i][j] - 'a' + 1];
H[i][j] = Hash(cnt[i][j]);
val[i][j] = val[i][j - 1] ^ (1 << (s[i][j] - 'a'));
}
}
for (int i = 1; i <= M; ++i) {
for (int j = i; j <= M; ++j) {
for (int k = 1; k <= N; ++k) {
t[k * 2 - 1] = 0;
if (check(k, i, j))
t[k * 2] = (H[k][j] - H[k][i - 1] + mod) % mod;
else
t[k * 2] = -1ll * k;
}
manacher();
}
}
printf("%lld\n", ans);
return 0;
}
既然选择了远方,便只顾风雨兼程。