题目链接:https://codeforces.com/problemset/problem/253/D
题目大意:对于一个字符矩阵,找其中的子块,要求有两个其一子块中包含字符a的个数不超过k,其二子块的四个角的字符相同。且子块的行数大于等于2,列数大于等于2
Examples
Input
3 4 4
aabb
baab
baab
Output
2
Input
4 5 1
ababa
ccaca
ccacb
cbabc
Output
1
emmm,首先,最为暴力的方法就是直接枚举上边界、下边界、左边界和右边界,那么复杂度为$O(n^4)$会T掉,暴力写法如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <iostream> using namespace std; const int mac=450; char s[mac][mac]; int as[mac][mac]; int main(int argc, char const *argv[]) { freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n,m,lim; cin>>n>>m>>lim; for (int i=1; i<=n; i++) cin>>s[i]+1; for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) as[i][j]=as[i-1][j]+as[i][j-1]-as[i-1][j-1]+(s[i][j]=='a'?1:0); int ans=0; for (int i=1; i<n; i++){//枚举上行 for (int j=i+1; j<=n; j++){//枚举下行 for (int l=1; l<m; l++){//枚举左边界 for (int r=l+1; r<=m; r++){//枚举右边界 if (as[j][r]-as[i-1][r]-as[j][l-1]+as[i-1][l-1]>lim) continue; if (s[i][l]==s[i][r] && s[i][r]==s[j][r] && s[j][r]==s[j][l]) ans++; } } } } printf("%d ",ans); return 0; }
接下来我们就是想办法优化它了。枚举左边界的时候判断左上角和左下角的字符,优化一下,接下来就是对该列该宽度下的上下字符一样的情况,我们用数组来保存每个字符在的合法数量,直接将该字符对应的ASCLL位置进行+1操作:
for (int l=1; l<m; l++) { //枚举左边界 if (s[i][l]!=s[j][l]) continue; cnt[s[i][l]]--; while (r<=m && as[j][r]-as[i-1][r]-as[j][l-1]+as[i-1][l-1]<=lim) { if (s[i][r]==s[j][r]) cnt[s[j][r]]++; r++; } if (cnt[s[i][l]]>0) ans+=cnt[s[i][l]]; }
这样就大大优化了时间,其中cnt数组刚开始-1是由于r也是从1开始的,他需要去一下重。
那么优化后的代码也就出来了:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <iostream> using namespace std; typedef long long ll; const int mac=450; char s[mac][mac]; int as[mac][mac],cnt[200]; int main(int argc, char const *argv[]) { freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); int n,m,lim; cin>>n>>m>>lim; for (int i=1; i<=n; i++) cin>>s[i]+1; for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) as[i][j]=as[i-1][j]+as[i][j-1]-as[i-1][j-1]+(s[i][j]=='a'?1:0); ll ans=0; for (int i=1; i<n; i++){//枚举上行 for (int j=i+1; j<=n; j++){//枚举下行 memset(cnt,0,sizeof cnt); int r=1; for (int l=1; l<m; l++){//枚举左边界 if (s[i][l]!=s[j][l]) continue; cnt[s[i][l]]--; while (r<=m && as[j][r]-as[i-1][r]-as[j][l-1]+as[i-1][l-1]<=lim){ if (s[i][r]==s[j][r]) cnt[s[j][r]]++; r++; } if (cnt[s[i][l]]>0) ans+=cnt[s[i][l]]; } } } printf("%lld ",ans); return 0; }