牛客网多校(第七场) J题(好题)

版权声明:选经典题目,写精品文章. https://blog.csdn.net/nka_kun/article/details/81565282

题目描述 

You have a n * m grid of characters, where each character is an English letter (lowercase or uppercase, which means there are a total of 52 different possible letters).

A nonempty subrectangle of the grid is called sudoku-like if for any row or column in the subrectangle, all the cells in it have distinct characters. 

How many sudoku-like subrectangles of the grid are there?

输入描述:

The first line of input contains two space-separated integers n, m (1 ≤ n, m ≤ 1000).

The next n lines contain m characters each, denoting the characters of the grid. Each character is an English letter (which can be either uppercase or lowercase).

输出描述:

Output a single integer, the number of sudoku-like subrectangles.

示例1

输入

复制

2 3
AaA
caa

输出

复制

11

题意:问在给出的矩阵中,有多少个子矩阵,它内部的任意一行,任意一列的字母不重复.

思路:这个题还真挺好,这样的处理还真是没有想到,导致自己的方法改来改去还是超时.
我们完全可以把每个字符所能到达的最上端和最左端预处理出来(方便起见),扫一遍的复杂度.然后枚举每个点作为矩阵的右下端点,这样仿佛我们对于每个点,只需要往上一直扫能到达的每个点,把扫到的这个点能到达的最左端的距离加到答案里就好了,但是这样当然不对,下面这个样例.
3 3
fdh
abc
cbh
h点往左可以到达c,h上边的c点往左可以到达a,但是很容易发现b重复了.
但是我们想到,遍历到(3,3)的时候,(3,2)肯定已经遍历过了,所以那个时候我们
就从(3,2)往(2,2)走,也就是我们可以开一个数组left,表示这个点往左可以到达最左的长度,在把(3,2)当作矩阵的右下端点时,我们不会往(2,2)走,所以后边的(3,3)往上走到(2,3)时也不应该到达(2,2),所以刚才我们应该在遍历(3,2)时把(2,2)以及以上的点left清零.每次把某个点当作矩阵的右下端点时就把left数组更新一边即可.

代码:

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;

int n,m;
char mp[1005][1005];
int l[1005][1005],u[1005][1005],last[234];

int main()
{
	scanf("%d %d",&n,&m);
	for(int i = 1;i<= n;i++)
		for(int j = 1;j<= m;j++)
			scanf(" %c",&mp[i][j]);
	
	for(int i = 1;i<= n;i++)
	{
		mem(last,0);
		for(int j = 1;j<= m;j++)
		{
			l[i][j] = min(l[i][j-1]+1,j-last[mp[i][j]]);
			last[mp[i][j]] = j;
		}
	}
		
	for(int j = 1;j<= m;j++)
	{
		mem(last,0);
		for(int i = 1;i<= n;i++)
		{
			u[i][j] = min(u[i-1][j]+1,i-last[mp[i][j]]);
			last[mp[i][j]] = i;
		}
	}
	
	ll ans = 0;
	for(int i = 1;i<= n;i++)
	{
		int left[1005];
		mem(left,0);
		for(int j = 1;j<= m;j++)
		{
			int len = inf;
			for(int k = i;k>= i-u[i][j]+1;k--)
			{
				left[k] = min(left[k]+1,l[k][j]);
				len = min(len,left[k]);
				ans+= len;
			}
			for(int k = i-u[i][j];k>= i-55&&k>= 0;k--) left[k] = 0;//(精髓) 
		}
	}
	
	printf("%lld\n",ans);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/nka_kun/article/details/81565282