Codeforces 1080E-Sonya and Matrix Beauty
题意
给出一个N*M的矩阵,问其有多少个子矩阵是“美丽矩阵”。
美丽矩阵:
选定矩阵后,可以在对矩阵的每一行中的元素任意调换位置,使该矩阵的每一行、每一列均是回文串。
题解
先对每一行用计数数组哈希,并考虑是否能够构成回文串。然后考虑列,枚举左右端点,Manacher计算回文串数量。
代码
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&-x)
typedef long long ll;
const int N=1e3+10;
const int base=131;
const ll mod=1e9+7;
char s[N][N];
int a[N][N];
int cnt[N][N][30];
ll Hash[N][N];
ll val[N][N];
ll tmp[N<<1];
int n,m;
ll ans;
ll getHash(int i,int j){
ll res=0;
for(int k=1;k<=26;k++) res=(res*base%mod+cnt[i][j][k])%mod;
return res;
}
bool check(int k,int i,int j){
ll x=val[k][j]^val[k][i-1];
if(!x||!(x-lowbit(x))) return 1;
return 0;
}
int d[N];
void Manacher(){
memset(d,0,sizeof(d));
int l=1,r=0;
for(int i=1;i<=2*n+1;i++){
if(tmp[i]<0) continue;
int k=(i>r)?0:min(r-i,d[l+r-i]);
while(i-k>=1&&i+k<=2*n+1&&tmp[i-k]==tmp[i+k]) k++;
d[i]=k--;
if(i+k>r){
l=i-k;
r=i+k;
}
ans+=(ll)d[i]/2;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
a[i][j]=s[i][j]-'a'+1;
}
}
for(int i=1;i<=n;i++){
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][a[i][j]]++;
Hash[i][j]=getHash(i,j);
val[i][j]=val[i][j-1]^(1<<a[i][j]);
}
}
for(int i=1;i<=m;i++){
for(int j=i;j<=m;j++){
for(int k=1;k<=n;k++){
tmp[k*2-1]=0;
if(check(k,i,j)) tmp[k<<1]=(Hash[k][j]-Hash[k][i-1]+mod)%mod;
else tmp[k<<1]=-k;
}
tmp[2*n+1]=0;
Manacher();
}
}
printf("%lld\n",ans);
return 0;
}