题目链接:点击打开链接
题意,给出一个字符表,问有多少子表满足:四个角字符相同,子表内a字符的数量不超过k。
思路:不加入任何技巧的话,上行、下行、左列、右列四重枚举,O(n^4),复杂度太高,可以加入一点枚举技巧,减少一层枚举。
首先要利用前缀和预处理,a[i][j]表示第i行,1 ~ j列字符a的数量。
技巧的话,有点形似尺取法,但有区别,尺取法是每次移动一个坐标的le,然后尽可能右移rig,直到le == rig结束;此处是每次移动一个坐标的bottom(下行),当top(上行)位置不合适再下移,当bottom到达上限就结束,不用枚举到top == bottom的原因又用到了另一个技巧:开了一个count数组,记录当前子表每行中最左列等于最右列的数量,当bottom扫过去,之前每行的结果已经记录了,直接利用即可。
// CodeForces 253D Table with Letters - 2 运行/限制:624ms/2000ms
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
char map[405][405];//原表
int a[405][405];//前缀和,a[i][j]表示第i行,1 ~ j列字符a的数量
int count[30];//计算当前子表每行中最左列等于最右列的数量
int main(){
int n, m, k;
long long ans;
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
while (scanf("%d%d%d", &n, &m, &k) != EOF) {
ans = 0;
memset(a, 0, sizeof(a));
for (int i = 1; i <= n; i++) {
scanf("%s", map[i] + 1);
for (int j = 1; j <= m; j++) {
a[i][j] = a[i][j - 1] + (map[i][j] == 'a');//计算前缀和
}
}
for (int le = 1; le < m; le++) {//左列
for (int rig = le + 1; rig <= m; rig++) {//右列
int top = 1, bottom, sum = 0;//top:上行 bottom:下行 sum:字符a数量
memset(::count, 0, sizeof(::count));
for (bottom = top; bottom <= n; bottom++) {
sum += a[bottom][rig] - a[bottom][le - 1];//更新sum
if (map[bottom][le] == map[bottom][rig]) {//更新count数组
::count[map[bottom][le] - 'a']++;
}
while (sum > k && top <= bottom) {//sum超出范围,下移top
//下移前先处理sum和count数组
sum -= a[top][rig] - a[top][le - 1];
if (map[top][le] == map[top][rig]) {
::count[map[top][le] - 'a']--;
}
top++;
}
if (top < bottom && map[bottom][le] == map[bottom][rig]) {
ans += ::count[map[bottom][le] - 'a'] - 1;
}
}
}
}
printf("%I64d\n", ans);
}
return 0;
}