【CF1080E】【Manacher】Sonya and Matrix Beauty

题目链接: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);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值