zoukankan      html  css  js  c++  java
  • 奶牛矩阵

    题目

    题目

    解法

    老哥,数据是真的水啊,让我的暴力过了就不说了,还花了我一个小时时间证明了题解里面一个AC的代码是错的!!!

    首先,这些解法都基于一个特别基础的思想,就是这个最小覆盖矩阵的左上角一定在((1,1))的位置,即使不在(又没说最小覆盖矩阵只能有一个),我们也可以移动到((1,1))的位置(当然,这个时候这个最小覆盖矩阵就已经改变了)。

    如:(ABABA)(BA)是最小覆盖矩阵,那么往左移动,就变成了(AB),就是从((1,1))开始的了。

    同时最小覆盖矩阵可以分成行和列分开处理,这个是不难想到的,但是也是特别关键的思路。(一般矩阵不会就可以尝试分成行和列处理进行简单化处理)

    解法1

    暴力出奇迹!!!

    我们用Hash来判断行和列是否相等,然后对行和列分别处理,对于行,每次代一个(k)进行(pd),如果存在(i)使得第(i)行不等于(i-k)行,那么(pd)返回(false),否则为(true),此时最小覆盖矩阵的行数出来了,就是(k),列也是同理。

    时间复杂度:(O(R^2+C^2)),为什么能A?常数小,跑不满,重点是数据弱。

    //自然溢出为模数 
    #include<cstdio>
    #include<cstring>
    #define  N  11000
    #define  M  80
    using  namespace  std;
    typedef  unsigned  long  long  LL;
    LL  ha[N],hb[M],t=11;
    int  n,m; 
    char  a[N][M];
    bool  pd(LL  *b,int  k,int  li/*限制*/)
    {
        for(int  i=k+1;i<=li;i++)
        {
            if(b[i]!=b[i-k])//不是这个数字 
            {
                return  0;
            }
        }
        return  1;
    }
    int  find(LL  *b,int  li)
    {
        for(int  i=1;i<li;i++)
        {
            if(pd(b,i,li)==1)
            {
                return  i;
            }
        }
        return  li;
    }
    int  main()
    {
        scanf("%d%d",&n,&m);
        for(int  i=1;i<=n;i++)scanf("%s",a[i]+1);
        for(int  i=1;i<=n;i++)
        {
            for(int  j=1;j<=m;j++)ha[i]=ha[i]*t+(a[i][j]-'0'),hb[j]=hb[j]*t+(a[i][j]-'0');
        }
        printf("%d
    ",find(ha,n)*find(hb,m));
        return  0;
    }
    
    

    解法2(错误)

    https://www.acwing.com/solution/content/3102/这个博主讲的,Hake数据已经发在评论里面了。

    当然如果我理解错意思了就当我放了个屁。

    这个主要是提醒一下,在一个不完全覆盖的字符串中补齐来找循环节,但是有没有考虑过有可能存在多个循环节相互冲突,补齐很难考虑到所有的循环节的,举个栗子:(aabbbaaabbbaa)中,(aa)(aa)匹配,然后把(bbbaaabbb)补在了后面,但是这只是照顾到了循环节:(aabbbaaabbb),然而还有个更短的循环节(aabbba),这难道是启示我们补齐时要找最短的匹配吗?不,大概率不行。当然这里只是说明一下不能这么做罢了。

    花了我一个小时去hake啊QAQ,还有代码也出了不少问题。

    解法3

    还有一种做法就是以kmp来解决行和列的覆盖。

    不过把判断字符是否相等改成了判断行或列是否相等。

    时间复杂度:(O(RC))

    这里主要利用了(kmp)(n-kmp[n])是循环节长度的性质(不管是不是精准覆盖。)

    这里不认真证了,几个提示:(kmp)的定义是(kmp[n])绝对是保证在最长的情况,同时可以证明(n-kmp[n])确实是字符串中的一个循环节,而且如果是循环节,绝对可以满足(kmp[n])中除了最大以外的其他定义,由最大也可以推得(n-kmp[n])是最小的循环节,得证。(当然只是草草一证。)

    代码采用https://www.acwing.com/video/100/评论中https://paste.ubuntu.com/p/kXNrbGcdhj/的

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 10010, M = 80;
    int n, m;
    char str1[N][M];
    char str2[M][N];
    int ne1[N];
    int ne2[N];
    int main()
    {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++ )
        {
            cin >> str1[i];
        }
        int f=1,s=0;//一维 二维
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<m;j++)
            {
                str2[f++][s]=str1[i][j];
            }
            f=1;s++;
        }
        for (int j = 0, i = 2; i <= m; i ++ )
        {
            while (j && strcmp(str2[j + 1], str2[i])) j = ne2[j];
            if (!strcmp(str2[j + 1], str2[i])) j ++ ;
            ne2[i] = j;
        }
        int width = m - ne2[m];
        for (int j = 0, i = 2; i <= n; i ++ )
        {
            while (j && strcmp(str1[j + 1], str1[i])) j = ne1[j];
            if (!strcmp(str1[j + 1], str1[i])) j ++ ;
            ne1[i] = j;
        }
        int height = n - ne1[n];
        cout << width * height << endl;
        return 0;
    }
    

    当然,把strcmp换成hash更快哟。

  • 相关阅读:
    Java线程基础(二)
    Java线程基础(一)
    泛型集合List的详细用法
    Java中日期格式(String、Date、Calendar)的相互转换
    重写Java中包装类的方法
    Java的集合框架(第一次小结)
    node.js 调用mysql 数据库
    win10 系统解决mysql中文乱码问题
    vue-echarts图表
    文件上传的几个例子
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/13385487.html
Copyright © 2011-2022 走看看