小美的字符串变换(卡码网周赛第二十一期(23年美团笔试真题))
题目描述
小美拿到了一个长度为 n 的字符串,她希望将字符串从左到右平铺成一个矩阵(先平铺第一行,然后是第二行,以此类推,矩阵有 x 行 y 列,必须保证 x * y=n,即每 y 个字符换行,共 x 行)。
该矩阵的权值定义为这个矩阵的连通块数量。小美希望最终矩阵的权值尽可能小,你能帮小美求出这个最小权值吗?
注:我们定义,上下左右四个方向相邻的相同字符是连通的。
输入
第一行输入一个正整数 n(1 <= n <= 10^4),代表字符串的长度。
第二行输入一个长度为 n 的、仅由小写字母组成的字符串。
输出
输出一个整数表示最小权值。
样例输入
9
aababbabb
样例输出
2
提示
平铺为 3 * 3 的矩阵:
aab
abb
abb
共有 2 个连通块,4 个 a 和 5 个 b。
题解1(C++版本)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
const int dx[] = {0, 1, -1, 0};
const int dy[] = {1, 0, 0, -1};
char s[N];
int n, Width, Lenth;
bool vis[N];
struct node{
int x, y;
}now;
bool check(int x, int y){
if(x <= 0 || x > Lenth || y <= 0 || y > Width || (x - 1)*Width + y <= 0 || (x - 1)*Width + y > n || vis[(x - 1)*Width + y]) return false;
return true;
}
void bfs(int x, int y){
queue<node> q;
vis[(x - 1)*Width + y] = 1;
q.push({x, y});
while(!q.empty()){
now = q.front(); q.pop();
char c = s[(now.x - 1)*Width + now.y];
for(int i = 0; i < 4; i++){
int tx = now.x + dx[i];
int ty = now.y + dy[i];
if(!check(tx, ty) || s[(tx - 1)*Width + ty] != c) continue;
vis[(tx - 1)*Width + ty] = 1;
q.push({tx, ty});
}
}
}
/*
8
aabaabab 针对 2 * 4 与 4 * 2 这两种情况来讲,长宽分别是2,4,但连通块数量却不同
1、 aaba 有6个连通块
abab
2、 aa 有4个连通块
ba
ab
ab
*/
int main(){
scanf("%d%s", &n, s + 1);
int ans = n;
for(int i = 1; i <= n; i++){
int sum = 0;
if(i*(n/i)==n){ // 长与宽可以构成一个长方形
memset(vis, 0, sizeof vis);
Lenth = i;
Width = n/i;
for(int lenth = 1; lenth <= Lenth; lenth++){
for(int width = 1; width <= Width; width++){
if(!vis[(lenth - 1)*Width + width]){
bfs(lenth, width);
sum++;
}
}
}
ans = min(ans, sum);
}
}
printf("%d\n", ans);
return 0;
}