为了更好的阅读体检,可以查看我的算法学习网
在线评测链接:P1444
题目内容
小美拿到了一个长度为 n n n的字符串,她希望将字符串从左到右平铺成一个矩阵(先平铺第一行,然后是第二行,以此类推,矩阵有 x x x行 y y y列,必须保证 x ∗ y = n x * y =n x∗y=n,即每 y y y个字符换行,共 x x x行)。
该矩阵的权值定义为这个矩阵的连通块数量。小美希望最终矩阵的权值尽可能小,你能帮小美求出这个最小权值吗?
注: 我们定义,上下左右四个方向相邻的相同字符是连通的。
输入描述
第一行输入一个正整数 n n n,代表字符串的长度。
第二行输入一个长度为 n n n的、仅由小写字母组成的字符串。
1 ≤ n ≤ 1 0 4 1 \leq n \leq 10^4 1≤n≤104
输出描述
输出一个整数表示最小权值。
样例
输入输出示例仅供调试,后台判题数据一般不包含示例
输入
9
aababbabb
输出
2
说明
平铺为 3 ∗ 3 3*3 3∗3的矩阵:
a a b aab aab
a b b abb abb
a b b abb abb
共有 2 2 2个连通块, 4 4 4个 a a a和 5 5 5个 b b b。
思路:枚举+bfs
首先看这个数据范围,可以知道的是一个数的因子数不会太大。暴力枚举可以发现,10000以内因子数最大的数是 9240 9240 9240 ,共有 64 64 64 个因子。
而 64 64 64 一般就是 log n \log n logn 的最大值。
所以可以枚举因子来确定矩阵的行数和列数。
这样最多 64 64 64 次 B F S BFS BFS 求连通块即可。
时间复杂度: O ( 64 n ) O(64n) O(64n)
import java.util.*;
public class Main {
static class Pair {
int first, second;
Pair(int first, int second) {
this.first = first;
this.second = second;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int len = scanner.nextInt();
String s = scanner.next();
int[] vis = new int[len];
int[] dx = {-1, 0, 1, 0};
int[] dy = {0, 1, 0, -1};
int mid = len / 2;
int ans = 0x3f3f3f3f;
for (int i = 1; i <= mid; ++i) {
if (len % i == 0) {
ans = Math.min(ans, get(i, len / i, s, vis, dx, dy));
}
}
System.out.println(ans);
}
static int get(int n, int m, String s, int[] vis, int[] dx, int[] dy) {
Queue<Pair> q = new LinkedList<>();
int res = 0;
Arrays.fill(vis, 0);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (vis[i * m + j] != 0) {
continue;
}
char ch = s.charAt(i * m + j);
q.add(new Pair(i, j));
while (!q.isEmpty()) {
Pair top = q.poll();
for (int k = 0; k < 4; ++k) {
int nx = top.first + dx[k];
int ny = top.second + dy[k];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && vis[nx * m + ny] == 0 && s.charAt(nx * m + ny) == ch) {
vis[nx * m + ny] = 1;
q.add(new Pair(nx, ny));
}
}
}
res += 1;
}
}
return res;
}
}