当前位置:   article > 正文

NC13610 矩阵

NC13610 矩阵

题目描述

给出一个n * m的矩阵。让你从中发现一个最大的正方形。使得这样子的正方形在矩阵中出现了至少两次。输出最大正方形的边长。

输入描述:

第一行两个整数n, m代表矩阵的长和宽;
接下来n行,每行m个字符(小写字母),表示矩阵;

输出描述:

输出一个整数表示满足条件的最大正方形的边长。

输入

5 10
ljkfghdfas
isdfjksiye
pgljkijlgp
eyisdafdsi
lnpglkfkjl

输出

3

备注:

对于30%的数据,n,m≤100;
对于100%的数据,n,m≤500;

题目分析

由数据规模可推出时间复杂度应不高于O(n^2log(n)))

如果暴力遍历,则时间复杂度为O(n^3)及以上。

考虑使用二维字符串哈希,便于快速比较两个不同的字符正方形是否相同;

同时,考虑遍历正方形边长时,使用二分查找方法。

题目代码

关于,二分查找也可以看下这篇文章,里面有些资源可以帮助理解。

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. using ull = unsigned long long; //使用ull可自动取模2^64
  4. const int N = 5e2 + 10;
  5. const int base1 = 131, base2 = 233; //用于哈希
  6. ull p1[N], p2[N];
  7. map<ull, int> mp; //统计哈希值为X的正方形个数
  8. char s[N][N]; //原字符数组
  9. ull h[N][N]; //哈希数组
  10. int n, m;
  11. //由于要求前缀和,所以下标均从1开始
  12. bool val(int x){ //求解
  13. mp.clear(); //记得清除映射
  14. for(int i = x; i <= n; ++i){
  15. for(int j = x; j <= m; ++j){
  16. ull k = h[i][j] - h[i-x][j]*p2[x] - h[i][j-x]*p1[x] + h[i-x][j-x]*p2[x]*p1[x];
  17. ++mp[k];
  18. if(mp[k] > 1) return true;
  19. }
  20. }
  21. return false; //不满足条件
  22. }
  23. int main(){
  24. cin >> n >> m;
  25. for(int i = 1; i <= n; ++i) scanf("%s", (s[i]+1));
  26. //1. 预处理哈希的幂次方, 用于后续字符串对齐
  27. p1[0] = p2[0] = 1; //
  28. for(int i = 1; i < N; ++i){
  29. p1[i] = p1[i-1] * base1;
  30. p2[i] = p2[i-1] * base2;
  31. }
  32. //2. 计算二维字符串前缀和
  33. //2.1 计算一维 行 前缀和
  34. for(int i = 1; i <= n; ++i)
  35. for(int j = 1; j <= m; ++j)
  36. h[i][j] = h[i][j - 1] * base1 + (s[i][j]-'a');
  37. //2.2 计算以(i,j)为右下角的二维 矩阵 前缀和
  38. for(int i = 1; i <= n; ++i)
  39. for(int j = 1; j <= m; ++j)
  40. h[i][j] = h[i-1][j] * base2 + h[i][j];
  41. //3. 二分法求解满足条件的正方形最大的边长
  42. // //此处二分法使用开区间方法 (l, r)为待查找区间
  43. // int l = 0, r = min(n,m)+1;
  44. // while(l < r-1){
  45. // int mid = l + (r-l)/2;
  46. // if(val(mid)) l = mid;
  47. // else r = mid;
  48. // } // 出来时, l == r-1
  49. // //[0, l]满足条件, ..., [r, min(n,m)]不满足条件
  50. // //l即为所求
  51. // cout << l << endl;
  52. // //此处二分法使用闭区间方法 [l,r]为待查找区间
  53. // int l = 1, r = min(n, m);
  54. // while(l <= r){
  55. // int mid = l + (r-l)/2;
  56. // if(val(mid)) l = mid+1;
  57. // else r = mid-1;
  58. // } //出来时,r == l-1
  59. // //[0, l)满足条件,...,(r, min(n,m)]不满足条件
  60. // //l-1即为所求
  61. // cout << l-1 << endl;
  62. //此处二分法使用左开右闭区间方法 [l, r)为待查找区间
  63. int l = 1, r = min(n,m)+1;
  64. while(l < r){
  65. int mid = l + (r-l)/2;
  66. if(val(mid)) l = mid+1;
  67. else r = mid;
  68. } //出来时,l == r
  69. //[0, l)均满足条件,...,[r, min(n,m)]均不满足条件
  70. //l-1即为所求
  71. cout << l-1 << endl;
  72. return 0;
  73. }

三种二分查找均已通过!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/360532
推荐阅读
相关标签