奶牛矩阵

题目

题目

解法

老哥,数据是真的水啊,让我的暴力过了就不说了,还花了我一个小时时间证明了题解里面一个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\n",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更快哟。

posted @ 2020-07-27 15:02  敌敌畏58  阅读(143)  评论(0编辑  收藏  举报