题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4029
题意:给定一个n*m的字母矩阵。求有多少个不同的子矩阵?
思路:枚举列数w,hash[i][j]保存第i行从第j列开始宽度为w的串的哈希值,然后以列为串,也就是有m-w+1个串建立自动机。求不同子串就是宽度为w时的不同的矩阵个数。
int r[N],sa[N],wa[N],wb[N],wd[N],rank[N],h[N];
int cmp(int *r,int a,int b,int len)
{
return r[a]==r[b]&&r[a+len]==r[b+len];
}
void da(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
FOR0(i,m) wd[i]=0;
FOR0(i,n) wd[x[i]=r[i]]++;
FOR1(i,m-1) wd[i]+=wd[i-1];
FORL0(i,n-1) sa[--wd[x[i]]]=i;
for(j=1,p=1;p<n;j<<=1,m=p)
{
p=0;
FOR(i,n-j,n-1) y[p++]=i;
FOR0(i,n) if(sa[i]>=j) y[p++]=sa[i]-j;
FOR0(i,m) wd[i]=0;
FOR0(i,n) wd[x[i]]++;
FOR1(i,m-1) wd[i]+=wd[i-1];
FORL0(i,n-1) sa[--wd[x[y[i]]]]=y[i];
t=x;x=y;y=t;p=1;x[sa[0]]=0;
FOR1(i,n-1) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
void calHeight(int *r,int *sa,int n)
{
int i,j,k=0;
FOR1(i,n) rank[sa[i]]=i;
FOR0(i,n)
{
if(k) k--;
j=sa[rank[i]-1];
while(i+k<n&&j+k<n&&r[i+k]==r[j+k]) k++;
h[rank[i]]=k;
}
}
int n,m,b[N];
char s[N],s1[140][140];
u64 hash[140][140];
map<u64,int> mp;
int Max;
void init(int w)
{
mp.clear(); Max=0;
int i,j,k;
FOR0(i,n) FOR0(j,m-w+1)
{
hash[i][j]=0;
for(k=j;k<j+w;k++)
{
hash[i][j]=hash[i][j]*107+s1[i][k];
}
if(mp.find(hash[i][j])==mp.end())
{
mp[hash[i][j]]=++Max;
}
}
}
int M;
u64 getCnt(int colCnt)
{
u64 ans=(i64)M*(M+1)/2;
int i;
FOR1(i,M) ans-=h[i];
FOR1(i,colCnt)
{
M-=n;
ans-=(i64)(n+1)*M;
M--;
}
return ans;
}
u64 cal(int w)
{
init(w);
int L=0,i,j;
FOR0(j,m-w+1)
{
FOR0(i,n) r[L++]=mp[hash[i][j]];
if(j<m-w) r[L++]=++Max;
}
r[L]=0;
da(r,sa,L+1,Max+1);
calHeight(r,sa,L);
M=L;
u64 temp=getCnt(m-w+1);
return temp;
}
int main()
{
int C,num=0;
RD(C);
while(C--)
{
RD(n,m);
int i;
u64 ans=0;
FOR0(i,n) RD(s1[i]);
FOR1(i,m) ans+=cal(i);
printf("Case #%d: %I64u\n",++num,ans);
}
return 0;
}