题目链接:Sonya and Matrix Beauty
【题意】:
提出了一个“美丽矩阵”的概念,何为“美丽”:就是这个矩阵无论是行还是列都是回文的。给定你N行M列的矩阵,问有多少个美丽子矩阵。矩阵的大小最小可以为1*1。
操作:可以通过调换行的字母顺序。
是:选定子矩阵,然后才能进行操作,最后统计
而非:操作后,选子矩阵,再统计
【题解】:
一开始不知道Mannacher,后来去学了,这个题目简直就是斩瓜切菜。不过需要大家注意细节。
细节1:
输入的都是小写字母26个。
细节2:
我们只能通过操作实现,但是操作是可以控制的,我们想达到回文,必定是26个字母至多有一种字母为奇数。
细节3:
选取子矩阵,我们是控制左右端点(两列之间),然后枚举上下。
细节4:
其实我们控制在两列之间是可以操作的,但是控制列回文并不是那么好实现。这里有一个同义转换的过程,就是列回文,就相当于把每一行当作一个单元。列回文过程中:判断两个单元是否相等。(两个单元相等 <=> 两行的26个字母数一一对应)。
最后的是求回文,回文就是用Manacher,但是重新写一遍“=”函数。计算Len数组,但是这个数组我们不是求它的长度,而是把长度整除2,因为关于中心对称说,确定了左右边界,确定了中心,最后就是把最长回文串半径就是它的上下边界。
贴上代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=5e2+10;
const int M=27;
int Cnt[N][M],Odd[N],Len[N],n,m,Ans;
char S[N][N];
bool Cmp(int a,int b){
if( Odd[a]>1 || Odd[b]>1 ) return false;
for(int i=0;i<26;i++) {
if (Cnt[a][i] != Cnt[b][i])
return false;
}
return true;
}
void Manacher(){
int Po=0,mx=0;
Len[0]=1;
for(int i=1;i<=2*n;i++){
if(i<=mx) Len[i]=min(mx-i,Len[2*Po-i]);
else Len[i]=1;
while( i-Len[i]>=0 && i+Len[i]<=2*n+1 && Cmp(i+Len[i],i-Len[i]) )
Len[i]++;
if(Odd[i]>1) Len[i]=1;
if( mx < i+Len[i] ){
Po=i;
mx=i+Len[i];
}
Ans+=(Len[i])/2;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",S[i]+1);
}
for(int L=1;L<=m;L++){ //枚举左端点
for(int i=1;i<=n;i++){ //因为每次枚举的区间不一样,所以需要初始化
//初始化的内容是:每一行26个字母的个数
for(int k=0;k<26;k++){
Cnt[i*2-1][k]=Odd[i*2-1]=0;
}
}
for(int R=L;R<=m;R++){ //枚举右端点
for(int i=1;i<=n;i++){ //因为右端点的加入,多了一列的字母,
// 每一行的字母个数随之改变
Cnt[2*i-1][S[i][R]-'a']++;
if(Cnt[2*i-1][S[i][R]-'a']&1)
Odd[2*i-1]++;
else
Odd[2*i-1]--;
}
Manacher();
}
}
printf("%d\n",Ans);
}