jzoj 4683. 【GDOI2017模拟8.11】矩阵 后缀数组+哈希表

题意:求一个矩阵中有多少不同的子矩阵


分析:枚举宽度 w,hash[i][j]表示,表示第 i 行,从第 j 个字符开始的 w 个字符的哈希值。然后把起点相同的列,连在一起,也就是竖着把每一列连在一起,列与列之间用一个特殊字符隔开,变成了一个串剩下的就是求这个串有多少个不同的子串。用后缀数组来求子串种数。


时间复杂度: O(n^3log^2n)


代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 120
#define MOD1 2477
#define MOD2 2969
#define ll long long
using namespace std;

int n,m,s[N*N],b[MOD1*MOD2],c[N*N],d[N*N],sa[N*N],rank[N*N],height[N*N],l[N*N],hash[N][N],hash1[N][N],hash2[N][N];
int v[MOD1][MOD2],cnt;
char c1[N],ch[N][N];

void getsa(int n,int m)
{
	for (int i=0;i<=m;i++) b[i]=0;
	for (int i=1;i<=n;i++) b[s[i]]++;
	for (int i=1;i<=m;i++) b[i]+=b[i-1];
	for (int i=n;i>=1;i--) c[b[s[i]]--]=i;
	int t=0;
	for (int i=1;i<=n;i++)
	{
		if (s[c[i]]!=s[c[i-1]]) t++;
		rank[c[i]]=t;
	}
	int j=1;
	while (j<=n)
	{
		for (int i=0;i<=n;i++) b[i]=0;
		for (int i=1;i<=n;i++) b[rank[i+j]]++;
		for (int i=1;i<=n;i++) b[i]+=b[i-1];
		for (int i=n;i>=1;i--) c[b[rank[i+j]]--]=i;
		for (int i=0;i<=n;i++) b[i]=0;
		for (int i=1;i<=n;i++) b[rank[i]]++;
		for (int i=1;i<=n;i++) b[i]+=b[i-1];
		for (int i=n;i>=1;i--) d[b[rank[c[i]]]--]=c[i];
		int t=0;
		for (int i=1;i<=n;i++)
		{
			if (rank[d[i]]!=rank[d[i-1]]||rank[d[i]]==rank[d[i-1]]&&rank[d[i]+j]!=rank[d[i-1]+j]) t++;
			c[d[i]]=t;
		}
		for (int i=1;i<=n;i++) rank[i]=c[i];
		if (t==n) break;
		j*=2;
	}
	for (int i=1;i<=n;i++) sa[rank[i]]=i;
}

void getheight(int n)
{
	int k=0;
	for (int i=1;i<=n;i++)
	{
		if (k) k--;
		int j=sa[rank[i]-1];
		while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) k++;
		height[rank[i]]=k;
	}
}

int getans(int n)
{
	int ans=0;
	for (int i=1;i<=n;i++)
		ans+=l[sa[i]]-height[i];
	return ans;
}

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%s",c1);
		for (int j=1;j<=m;j++)
			ch[i][j]=c1[j-1];
	}
	int ans=0;
	for (int w=1;w<=m;w++)
	{
		for (int i=1;i<=n;i++)
			for (int j=1;j<=m-w+1;j++)
			{
				hash1[i][j]=(hash[i][j]*26+ch[i][j+w-1]-'A')%MOD1;
				hash2[i][j]=(hash[i][j]*26+ch[i][j+w-1]-'A')%MOD2;
				int x=hash1[i][j],y=hash2[i][j];
				if (!v[x][y]) v[x][y]=++cnt;
				hash[i][j]=v[x][y];
			}
		int len=0,hehe=cnt;
		for (int j=1;j<=m-w+1;j++)
		{
			if (j>1)
			{
				s[++len]=++hehe;
				l[len]=0;
			}
			for (int i=1;i<=n;i++)
			{
				s[++len]=hash[i][j];
				l[len]=n-i+1;
			}
		}
		memset(rank,0,sizeof(rank));
		getsa(len,hehe);
		getheight(len);
		ans+=getans(len);
	}
	printf("%d",ans);
	return 0;
}




你可能感兴趣的:(jzoj 4683. 【GDOI2017模拟8.11】矩阵 后缀数组+哈希表)