题目内容
小美拿到了一个长度为nnn的字符串,她希望将字符串从左到右平铺成一个矩阵(先平铺第一行,然后是第二行,以此类推,矩阵有xxx行yyy列,必须保证x∗y=nx * y =nx∗y=n,即每yyy个字符换行,共xxx行)。
该矩阵的权值定义为这个矩阵的连通块数量。小美希望最终矩阵的权值尽可能小,你能帮小美求出这个最小权值吗?
注: 我们定义,上下左右四个方向相邻的相同字符是连通的。
输入描述
第一行输入一个正整数nnn,代表字符串的长度。
第二行输入一个长度为nnn的、仅由小写字母组成的字符串。
1≤n≤1041 \leq n \leq 10^41≤n≤104
输出描述
输出一个整数表示最小权值。
样例
输入输出示例仅供调试,后台判题数据一般不包含示例
输入
9
aababbabb
输出
2
说明
平铺为3∗33*33∗3的矩阵:
aabaabaab
abbabbabb
abbabbabb
共有222个连通块,444个aaa和555个bbb。
思路:枚举+bfs
首先看这个数据范围,可以知道的是一个数的因子数不会太大。暴力枚举可以发现,10000以内因子数最大的数是 924092409240 ,共有 646464 个因子。
而 646464 一般就是 logn\log nlogn 的最大值。
所以可以枚举因子来确定矩阵的行数和列数。
这样最多 646464 次 BFSBFSBFS 求连通块即可。
时间复杂度:O(64n)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;
}
}