题目链接:http://poj.org/problem?id=2185

题目大意:求一个二维的字符串矩阵的最小覆盖子矩阵,即这个最小覆盖子矩阵在二维空间上不断翻倍后能覆盖原始矩阵。

题目分析:next函数的应用。需要枚举每一行每一列的字符串所对应的的 \(nxt[]\) 值,然后通过分析计算出最小的宽和最小的高。

具体分析

参考链接:https://blog.csdn.net/u013686535/article/details/52197467

一看这题,容易想出一种很直观的做法:求出每一行的最小重复串长度,取所有行的最小重复串长度的lcm为宽;对列也同样操作求出高。这种想法虽然很直观,但是否正确呢?

事实上,这种算法并不是正确的。如下面的这个反例:

2 8
ABCDEFAB
AAAABAAA

对于这个例子:第一行为6,第二行为5,6与5的最小公倍数为30,大于8则取8为宽,但明显是错误的。

但由于poj的测试数据太弱,以致使用这种方法的程序也可以通过。

下面介绍一下正解的做法。

首先是确定宽度:我们分别求出每行所有可能的重复子串长度,例如对于aaaa就有1、2、3和4,然后取每行都有的重复子串长度中最小的作为宽。

例如,对于上面的例子,第一行的重复子串长度只可能是6或8(显然整个串为一个重复子串也是可以的),第二行则可能是5、6、7或8,那么取它们都有的6和8当中最小值6,就是最小覆盖子矩阵的宽。

然后分成两步:

  • 考虑到每行的列数比较小, \(1 \le C \le 75\) ,可以直接暴力枚举重复子串的长度,每列逐一检查是否与当前枚举的重复子串相对应。因此求宽度这个部分的时间复杂度为 \(O(R \times C^2)\) 。
  • 找到C以后我们再来一个个枚举R就可以了,时间复杂度也是 \(O(R \times C^2)\) 。

不过我这里采用标记的方式,时间复杂度降到 \(O(R \times C)\) 。

实现代码如下:

#include <iostream>
#include <string>
#include <cstring>
#include <vector>
using namespace std;
const int maxn = 404000; int m, nxt[maxn];
string t; int N, M;
char ch[10010][80];
int cnt_c[80], cnt_r[10010]; void cal_next() {
m = t.length();
for (int i = 0, j = -1; i < m; i ++) {
while (j != -1 && t[j+1] != t[i]) j = nxt[j];
nxt[i] = (j+1 < i && t[j+1] == t[i]) ? ++j : -1;
}
} void solve() {
// 首先清空用于记录的cnt_c和cnt_r数组
memset(cnt_c, 0 ,sizeof(cnt_c));
memset(cnt_r, 0, sizeof(cnt_r));
// 遍历每一行
for (int i = 0; i < N; i ++) {
t = "";
for (int j = 0; j < M; j ++) t += ch[i][j];
cal_next();
int j = m-1;
while (nxt[j] != -1) {
j = nxt[j];
cnt_c[m-1-j] ++;
}
}
// 遍历每一列
for (int i = 0; i < M; i ++) {
t = "";
for (int j = 0; j < N; j ++) t += ch[j][i];
cal_next();
int j = m-1;
while (nxt[j] != -1) {
j = nxt[j];
cnt_r[m-1-j] ++;
}
}
// 找到最小的cnt_c[i]==N,没有则M
int cc = 1, rr = 1;
for (; cc < M; cc ++) if (cnt_c[cc] == N) break;
// 找到最小的cnt_r[i]==M,没有则N
for (; rr < N; rr ++) if (cnt_r[rr] == M) break;
cout << rr * cc << endl;
} int main() {
while (cin >> N >> M) {
for (int i = 0; i < N; i ++) cin >> ch[i];
solve();
}
return 0;
}

作者:zifeiy

最新文章

  1. BPM配置故事之案例14-数据字典与数据联动
  2. HDU 4715:Difference Between Primes
  3. 在包a中编写一个类Father,具有属性:年龄(私有)、姓名(公有); 具有功能:工作(公有)、开车(公有)。 在包a中编写一个子类Son,具有属性:年龄(受保护的)、姓名; 具有功能:玩(私有)、学习(公有)。 最后在包b中编写主类Test,在主类的main方法中测试类Father与类Son。
  4. 黄聪:如何使用WebKitBrowser调用元素点击事件(C#)
  5. iOS 之 多线程一
  6. window下手动搭建 PHP+Nginx+Mysql(转)
  7. shell脚本练习(随机取名)
  8. [js插件开发教程]一步步开发一个可以定制配置的隔行变色小插件
  9. 收集nodejs经典组件:
  10. 如何打开iPhone 中的heic格式照片
  11. MySql cmd下的学习笔记 —— 有关视图的操作(建立表)
  12. Confluence 6 用户目录图例 - 只读连接 LDAP 整合本地用户组
  13. cf1061D 贪心+multiset 好题!
  14. JavaScript原型之路
  15. python之路---03 整型 bool 字符串 for循环
  16. mongodb的优缺点
  17. Linux 移动或重命名文件/目录-mv 的10个实用例子
  18. 子查询 in 潜在的问题 - 建议最好别用
  19. httpSession的正确理解
  20. windows多线程(八) 信号量Semaphore

热门文章

  1. 机器学习中的那些树——决策树(三、CART 树)
  2. 2017年Android SDK下载安装及配置教程(附带原文地址)
  3. mysql全连接
  4. Springboot项目下mybatis报错:Invalid bound statement (not found)
  5. 【水滴石穿】rnTest
  6. R语言实现分层抽样(Stratified Sampling)以iris数据集为例
  7. SpringBoot Actuator监控【转】
  8. 模板内置函数(HTML)
  9. HTTP请求模型
  10. 字符缓冲流 Day20