zoukankan      html  css  js  c++  java
  • Codeforces Round #524 (Div. 2) E. Sonya and Matrix Beauty(字符串哈希,马拉车)

    https://codeforces.com/contest/1080/problem/E

    题意

    有一个n*m(<=250)的字符矩阵,对于每个子矩阵的每一行可以任意交换字符的顺序,使得每一行每一列都是一个回文串,问最多有多少个这样的子矩阵

    思路

    • 首先可以思考如何暴力,枚举四个边界(子矩阵),然后判定一下子矩阵的每一行每一列,这样的复杂度是nmnmn*m
    • 很明显需要找一下规律来优化一下复杂度,
      1. 对于每一行来说,最多只能存在一种字母的数量是奇数,这样一定可以保证这一行是回文串
      2. 对于每一列来说,因为不能交换顺序,所以必须具有回文性质(关于中心点对称),这就需要要求每一行相同的字母数量应该相同
    • 根据第二个规律,因为只需要验证每一行是否相同,所以可以采用字符串hash每一行,O(n*m)处理,O(1)验证
    • 首先枚举列的两个端点 O(mm),然后o(n)找出所有连续的回文行,跑马拉车算法,求出连续的回文行中所有的回文子行,O(n),总复杂度O(mm*n)
    • 符合回文性质的都可以用马拉车求出最长回文字串,回文字串个数
    #include<bits/stdc++.h>
    #define ll long long 
    #define M 605
    #define key 233
    #define P 1000000007
    using namespace std;
    int n,m,i,j,a[M][M],c1,c2,l1,l2,msk[M],cnt[M][30],x,ans=0,d1[M],d2[M];
    char s[M];
    ll hs[M],p[M],tp[M];
    
    int ck(int x){
        return (x==0)||((x&(x-1))==0);
    }
    void init(){
        p[0]=1;for(int i=1;i<=30;i++)p[i]=p[i-1]*key%P;
    }
    
    int cal(int l1,int l2){
        int n=0,ans=0;
        for(int i=l1;i<=l2;i++)tp[++n]=hs[i];
        int l=0,r=0,x;
        for(int i=1;i<=n;i++){
            if(i>r)x=1;
            else x=min(d1[l+r-i],r-i);
            while(i-x>=1&&i+x<=n&&tp[i-x]==tp[i+x])x++;
            d1[i]=x;
            ans+=d1[i];
            if(i+x-1>r){r=i+x-1;l=i-x+1;}
        }
        l=r=0;
        for(int i=1;i<=n;i++){
            if(i>r)x=0;
            else x=min(d2[l+r-i+1],r-i+1);
            while(i-x-1>=1&&i+x<=n&&tp[i-x-1]==tp[i+x])x++;
            d2[i]=x;
            ans+=d2[i];
            if(i+x>=r){l=i-x;r=i+x-1;}
        }
        return ans;
    }
    int main(){
        init();
        cin>>n>>m;
        for(i=1;i<=n;i++){
            scanf("%s",s+1);
            for(j=1;j<=m;j++){
                a[i][j]=s[j]-'a';
            }
        }
        for(c1=1;c1<=m;c1++){
            for(i=1;i<=n;i++){
               hs[i]=0;msk[i]=0;
               memset(cnt[i],0,sizeof(cnt[i]));
            }
            for(c2=c1;c2<=m;c2++){
               for(i=1;i<=n;i++){
                  x=a[i][c2];
                  if(cnt[i][x]){
                     hs[i]-=cnt[i][x]*p[x+1]%P;
                     if(hs[i]<0)hs[i]+=P;
                  }
                  cnt[i][x]++;
                  hs[i]+=cnt[i][x]*p[x+1]%P;hs[i]%=P;
                  msk[i]^=(1<<x);
               }
               l1=1;
               while(l1<=n){
                 if(!ck(msk[l1])){l1++;continue;}
                 l2=l1;
                 while(l2<=n&&ck(msk[l2]))l2++;
                 l2--;
                 ans+=cal(l1,l2);
                 l1=l2+1;
               } 
            } 
        }
        cout<<ans;
    }
    
  • 相关阅读:
    学 Win32 汇编[28]
    C#将数据集DataSet中的数据导出到EXCEL文件的几种方法
    FFmpeg 学习(四):FFmpeg API 介绍与通用 API 分析
    Android 代码混淆配置总结
    Android 视频播放器 (三):使用NBPlayer播放直播视频
    Android 视频播放器 (二):使用MediaPlayer播放视频
    Android 展示控件之Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系
    Android 视频播放器 (一):使用VideoView播放视频
    JavaCV 学习(二):使用 JavaCV + FFmpeg 制作拉流播放器
    Android Studio 常见问题及解决方法
  • 原文地址:https://www.cnblogs.com/VIrtu0s0/p/10043884.html
Copyright © 2011-2022 走看看