JZOJ4683 矩阵

题目大意

给一个n*m的字符矩阵,求有多少个不同的子矩阵,字符集为大写英文字母。
n,m<=110

分析

先往哈希上面想想,直接暴力搞每一个矩阵的哈希值再判重是 n6 ,然后就开始优化。首先确定宽度,行数就可以递增地继承信息。而宽度的递增,也是可以继承的,最后就可以 n4
实际上,我们确定宽度width以后,就可以转化为新矩阵,其中每一个元素都是原矩阵1*width的子矩阵。这样我们只用统计整个矩阵里,k*1的不同子矩阵个数。把它们打横来看,就是数个字符串了。用特殊字符#把数个字符串连接起来,问题变成求整个字符串里,不包含#的不同子串有多少个。
SA或者SAM解决了。

写题

智商捉急啊···一开始用特殊的哈希技巧来打70分,结果哈希太烂,小数据都会有误。
改题的时候,SA好久不打了,还得看模板,所以改题效率特别低。正确的姿势是回忆好SA,再开始打。然而我边打边看,不能专一做事,脑子一片混乱,调试也不高效。这是值得注意的地方。
还有一点,字符串哈希最好用多个质数模数。

贴个代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
typedef long long ll;
const int N=115,N2=24205,mo=1000000007,mo1=128473;
char ch;
int f[N][N],g[N][N],sa[N2],height[N2],x[N2],x1[N2],y[N2],wv[N2],ws[N2],n,m,i,j,k,l,a[N][N],res,rank[N2],tt,t1,p,pos,dur,ans,width;
struct rec
{
    int val,pos,val1;
}b[N2];
bool cmp(rec a,rec b)
{
    return a.val<b.val;
}
void getsa()
{
    fo(i,1,tt) wv[i]=0;
    fo(i,1,tt) wv[x[i]]++;
    fo(i,1,tt) wv[i]+=wv[i-1];
    fd(i,tt,1)
    {
        sa[wv[x[i]]]=i;
        wv[x[i]]--;
    }
    j=1;
    while (j<=tt)
    {
        p=0;
        fo(i,tt-j+1,tt) y[++p]=i;
        fo(i,1,tt)
            if (sa[i]>j)
                y[++p]=sa[i]-j;
        fo(i,1,tt) wv[i]=0;
        fo(i,1,tt) ws[i]=x[y[i]];
        fo(i,1,tt) wv[ws[i]]++;
        fo(i,1,tt) wv[i]+=wv[i-1];
        fd(i,tt,1)
        {
            sa[wv[ws[i]]]=y[i];
            wv[ws[i]]--;
        }
        p=1;
        fo(i,1,tt) y[i]=x[i];
        x[sa[1]]=1;
        fo(i,2,tt)
        {
            if (y[sa[i]]!=y[sa[i-1]]||y[sa[i]+j]!=y[sa[i-1]+j])
                p++;
            x[sa[i]]=p;
        }
        j*=2;
    }
}
void getheight()
{
    fo(i,1,tt) rank[sa[i]]=i;
    k=0;
    fo(i,1,tt)
    {
        if (k) k--;
        j=sa[rank[i]-1];
        while (j+k<=tt&&i+k<=tt&&x1[j+k]==x1[i+k]) k++;
        height[rank[i]]=k;
    }
}
void getans()
{
    fo(i,1,tt)
    {
        pos=sa[i];
        dur=((sa[i]-1)/(n+1)+1)*(n+1)-pos;
        ans+=max(dur-height[i],0);
    }
}
int main()
{
    freopen("matrix.in","r",stdin);
//  freopen("matrix.out","w",stdout);
    scanf("%d%d\n",&n,&m);
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            scanf("%c",&ch);
            a[i][j]=ch;
        }
        scanf("\n");
    }
    fo(width,1,m)
    {
        tt=0;
        res=m-width+1;
        fo(i,1,n) fo(j,1,res) 
        {
            g[i][j]=0;
            f[i][j]=(ll)(f[i][j]*13531+a[i][j+width-1])%mo;
            b[++tt].val=f[i][j];
            b[tt].pos=i*m+j-1;
        }
        sort(b+1,b+1+tt,cmp);
        b[0].val=-1;
        t1=0;
        fo(i,1,tt)
        {
            if (b[i].val!=b[i-1].val) t1++;
            g[b[i].pos/m][b[i].pos%m+1]=t1;
        }
        tt=0;
        fo(j,1,res)
        {
            fo(i,1,n)
                x[++tt]=g[i][j];
            x[++tt]=t1+1;
        }
        fo(i,1,tt) x1[i]=x[i];
        getsa();
        getheight();
        getans();
        fo(i,1,tt+4) y[i]=wv[i]=ws[i]=sa[i]=rank[i]=height[i]=x[i]=x1[i]=0;
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值