题目:戳这里
题意:给一个n*m的矩阵,里面由a~z及A~Z构成,问有多少个子矩阵满足任意一行或一列中都没有相同的字母。
解题思路:左上角和右下角两点可以确定一个矩阵。可以先预处理出来每个点作为一个矩阵的右下角,向左和向上的最长值。然后遍历每个点是右下角的情况,计算该点为右下角时,能构成多少个矩阵。计算方法为:
1.设右下角为(i,j),它向左的最长值为r[i][j],向上最长之为c[i][j],设左上角为(x,y)。
2.遍历j~j-r[i][j]+1,维护最小值minn[]。
3.根据minn[]数组和c[][]数组,找到符合条件的左上角,计入答案。
这三步操作的原因是,一个符合条件的左上角(x,y),要满足矩形底边上所有点的i-c[i][k]+1>=x,右边上所有点的j-r[k][j]+1>=y。
代码思路比较绕,写的时候得静下心。
附ac代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <string> 6 #include <cmath> 7 #include <map> 8 9 using namespace std; 10 typedef long long ll; 11 const int maxn = 1e3 + 10; 12 char st[maxn][maxn]; 13 int r[maxn][maxn], c[maxn][maxn]; 14 int pos[maxn]; 15 int minn[maxn]; 16 const int inf = 0x3f3f3f3f; 17 int main() 18 { 19 int n, m; 20 scanf("%d %d", &n, &m); 21 for(int i = 1; i <= n; ++i) 22 { 23 scanf("%s", st[i] + 1); 24 } 25 int len = 0; 26 int cnt = 0; 27 for(int i = 1; i <= n; ++i) 28 { 29 memset(pos, 0, sizeof(pos)); 30 for(int j = 1; j <= m; ++j) 31 { 32 len = st[i][j] - 'A'; 33 r[i][j] = j - pos[len]; 34 r[i][j] = min(r[i][j], r[i][j - 1] + 1);//避免abba时,无法更新pos 35 pos[len] = j; 36 } 37 } 38 for(int j = 1; j <= m; ++j) 39 { 40 memset(pos, 0, sizeof(pos)); 41 for(int i = 1; i <= n; ++i) 42 { 43 len = st[i][j] - 'A'; 44 c[i][j] = i - pos[len]; 45 c[i][j] = min(c[i][j], c[i - 1][j] + 1);//同上 46 pos[len] = i; 47 } 48 } 49 ll ans = 0; 50 for(int i = 1; i <= n; ++i) 51 { 52 memset(minn, inf, sizeof(minn)); 53 for(int j = 1; j <= m; ++j) 54 { 55 for(int k = j; k >= j - r[i][j] + 1; --k) 56 minn[k] = min(minn[k + 1], c[i][k]);//(i,k)点能取到的最远列 57 58 int len = j - r[i][j] + 1; 59 60 for(int k = i; k >= i - c[i][j] + 1; --k) 61 { 62 while(minn[len] < i - k + 1 || r[k][j] < j - len + 1)//倘若点(i,j)的minn值取不到k或(k,j)本身取不到minn,则说明len不符合条件 63 { 64 ++ len; 65 if(len > j) break; 66 } 67 if(len > j) break; 68 ans += j - len + 1; 69 // printf("%lld ans ", ans); 70 } 71 //printf("%d ans ",ans); 72 } 73 } 74 printf("%lld ", ans); 75 return 0; 76 }