概述
题目
给你两个整数数组 source 和 target ,长度都是 n 。还有一个数组 allowedSwaps ,其中每个 allowedSwaps[i] = [ai, bi] 表示你可以交换数组 source 中下标为 ai 和 bi(下标从 0 开始)的两个元素。注意,你可以按 任意 顺序 多次 交换一对特定下标指向的元素。
相同长度的两个数组 source 和 target 间的 汉明距离 是元素不同的下标数量。形式上,其值等于满足 source[i] != target[i] (下标从 0 开始)的下标 i(0 <= i <= n-1)的数量。
在对数组 source 执行 任意 数量的交换操作后,返回 source 和 target 间的 最小汉明距离 。
示例 1:
输入:source = [1,2,3,4], target = [2,1,4,5], allowedSwaps = [[0,1],[2,3]]
输出:1
解释:source 可以按下述方式转换:
- 交换下标 0 和 1 指向的元素:source = [2,1,3,4]
- 交换下标 2 和 3 指向的元素:source = [2,1,4,3]
source 和 target 间的汉明距离是 1 ,二者有 1 处元素不同,在下标 3 。
示例 2:
输入:source = [1,2,3,4], target = [1,3,2,4], allowedSwaps = []
输出:2
解释:不能对 source 执行交换操作。
source 和 target 间的汉明距离是 2 ,二者有 2 处元素不同,在下标 1 和下标 2 。
示例 3:
输入:source = [5,1,2,4,3], target = [1,5,4,2,3], allowedSwaps = [[0,4],[4,2],[1,3],[1,4]]
输出:0
提示:
n == source.length == target.length
1 <= n <= 105
1 <= source[i], target[i] <= 105
0 <= allowedSwaps.length <= 105
allowedSwaps[i].length == 2
0 <= ai, bi <= n - 1
ai != bi
解析
首先,我们来分析所谓的交换,有怎样的性质。因为我们执行交换的顺序和次数都是任意的,因此,如果0,1可以互相交换,而1,2可以互相交换,那就代表着0,1,2都可以互相交换。那么,如果我们单独看0,1,2这几个位置,那么这三个位置的汉明距离其实取决于source和target之间有多少个相异的字母。那么,我们要解决这个问题,其实可以看下整个数据的下标可以分成几个类似于0,1,2这样完全可以交换的组,而每个组中又有多少个相异的字母。接着,我们就想到,其实这个问题可以看作0,1,...,n都是图的节点,0,1可交换,就代表图的节点间是相连的,而我们的问题就是求这个图中有哪几个连通分量,到这里,解决问题的手段就很明显了,我们使用并查集,来寻找所有的联通分量。
于是就有了我第一个版本的代码。
class Solution {
class UnionFind {
private int[] id;
private int count;
private int[] sz;
public UnionFind(int N) {
count = N;
id = new int[N];
sz = new int[N];
for(int i = 0; i < N; i++) {
id[i] = i;
sz[i] = 1;
}
}
public int getCount() {
return count;
}
public boolean connected(int p, int q) {
return find(p) == find(q);
}
public int find(int p) {
if (p != id[p]) id[p] = find(id[p]);
return id[p];
}
public void union(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if(pRoot == qRoot) return;
if(sz[pRoot] < sz[qRoot]) { id[pRoot] = qRoot; sz[qRoot] += sz[pRoot]; }
else { id[qRoot] = pRoot; sz[pRoot] += sz[qRoot]; }
count--;
}
}
public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
int n = source.length;
UnionFind uf = new UnionFind(n);
for(int i=0;i<allowedSwaps.length;i++){
uf.union(allowedSwaps[i][0],allowedSwaps[i][1]);
}
int res = 0;
boolean[] visited = new boolean[n];
for(int i=0;i<n;i++){
if(visited[i]){
continue;
}
Set<Integer> s = new HashSet<Integer>();
Set<Integer> t = new HashSet<Integer>();
s.add(source[i]);
t.add(target[i]);
for(int j=i+1;j<n;j++){
if(uf.connected(i,j)){
s.add(source[j]);
t.add(target[j]);
visited[j] = true;
}
}
Set<Integer> ex = new HashSet<Integer>();
ex.addAll(s);
ex.removeAll(t);
res = res + ex.size();
}
return res;
}
}
很可惜,这个代码只要报了解答错误。错在哪里呢?错在source和target中都有重复的元素,而我使用set求差集是默认把重复元素全部忽略掉的。因此,需要用更复杂的数据结构,我想到,我可以用一个HashMap存储source元素和它出现的次数。对于target呢我先用一个数组存储所有元素,然后一一比对,遇到相异的就把结果+1,而对应元素出现次数-1,这样就避免了set忽略重复元素的情况。这就是我第二个版本的代码。
class Solution {
class UnionFind {
public int[] id;
private int count;
private int[] sz;
public UnionFind(int N) {
count = N;
id = new int[N];
sz = new int[N];
for(int i = 0; i < N; i++) {
id[i] = i;
sz[i] = 1;
}
}
public int getCount() {
return count;
}
public boolean connected(int p, int q) {
return find(p) == find(q);
}
public int find(int p) {
if (p != id[p]) id[p] = find(id[p]);
return id[p];
}
public void union(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if(pRoot == qRoot) return;
if(sz[pRoot] < sz[qRoot]) { id[pRoot] = qRoot; sz[qRoot] += sz[pRoot]; }
else { id[qRoot] = pRoot; sz[pRoot] += sz[qRoot]; }
count--;
}
}
public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
int n = source.length;
UnionFind uf = new UnionFind(n);
for(int i=0;i<allowedSwaps.length;i++){
uf.union(allowedSwaps[i][0],allowedSwaps[i][1]);
}
Set<Integer> connectedComponent = new HashSet<Integer>();
int res = 0;
for(int i=0;i<n;i++){
int root = uf.find(i);
connectedComponent.add(root);
}
for(int i:connectedComponent){
Map<Integer, Integer> s = new HashMap<Integer, Integer>();
ArrayList<Integer> t = new ArrayList<Integer>();
s.put(source[i], 1);
t.add(target[i]);
for(int j=0;j<n;j++){
if(i==j) continue;
if(uf.connected(i,j)){
if(s.containsKey(source[j])){
int value = s.get(source[j]);
s.put(source[j], value+1);
} else {
s.put(source[j], 1);
}
t.add(target[j]);
}
}
for(int j=0;j<t.size();j++){
int value = t.get(j);
if(s.containsKey(value)){
int appearTime = s.get(value);
appearTime--;
if(appearTime==0){
s.remove(value);
} else {
s.put(value, appearTime);
}
} else {
res++;
}
}
//for(int value:s.values()){
// res = res + value;
//}
}
return res;
}
}
很可惜,这段代码超时了,仔细看了一下,这里面确实有进一步优化的空间,没有必要有两个for循环。为了把其中一个for循环优化掉,我们需要Map<Integer, HashMap<Integer,Integer>>这么一个复杂的结构,key是当前节点对应的根节点,而后面的HashMap则继续存储我们前文中提到的数据。这样子,我们只需要一个for循环,就把所有的元素存储到我们希望的数据结构中去。最后再遍历一遍所有的key,把结果输出。搞定!
class Solution {
class UnionFind {
public int[] id;
private int count;
private int[] sz;
public UnionFind(int N) {
count = N;
id = new int[N];
sz = new int[N];
for(int i = 0; i < N; i++) {
id[i] = i;
sz[i] = 1;
}
}
public int getCount() {
return count;
}
public boolean connected(int p, int q) {
return find(p) == find(q);
}
public int find(int p) {
if (p != id[p]) id[p] = find(id[p]);
return id[p];
}
public void union(int p, int q){
int pRoot = find(p);
int qRoot = find(q);
if(pRoot == qRoot) return;
if(sz[pRoot] < sz[qRoot]) { id[pRoot] = qRoot; sz[qRoot] += sz[pRoot]; }
else { id[qRoot] = pRoot; sz[pRoot] += sz[qRoot]; }
count--;
}
}
public int minimumHammingDistance(int[] source, int[] target, int[][] allowedSwaps) {
int n = source.length;
UnionFind uf = new UnionFind(n);
for(int i=0;i<allowedSwaps.length;i++){
uf.union(allowedSwaps[i][0],allowedSwaps[i][1]);
}
Map<Integer, HashMap<Integer,Integer>> s = new HashMap<Integer, HashMap<Integer,Integer>>();
Map<Integer, ArrayList<Integer>> t = new HashMap<Integer, ArrayList<Integer>>();
int res = 0;
for(int i=0;i<n;i++){
int root = uf.find(i);
if(s.containsKey(root)){
HashMap<Integer,Integer> sMap = s.get(root);
if(sMap.containsKey(source[i])){
int value = sMap.get(source[i]);
sMap.put(source[i], value+1);
} else {
sMap.put(source[i], 1);
}
ArrayList<Integer> tList = t.get(root);
tList.add(target[i]);
t.put(root,tList);
} else {
HashMap<Integer,Integer> tmpMap = new HashMap<Integer,Integer>();
tmpMap.put(source[i],1);
s.put(root,tmpMap);
ArrayList<Integer> tmpList = new ArrayList<Integer>();
tmpList.add(target[i]);
t.put(root,tmpList);
}
}
for(int key:s.keySet()){
HashMap<Integer,Integer> sMap = s.get(key);
ArrayList<Integer> tList = t.get(key);
for(int i=0;i<tList.size();i++){
int value = tList.get(i);
if(sMap.containsKey(value)){
int appearTime = sMap.get(value);
appearTime--;
if(appearTime==0){
sMap.remove(value);
} else {
sMap.put(value, appearTime);
}
} else {
res++;
}
}
}
return res;
}
}
反思一下,好久不刷leetcode,感觉写代码的准确性,思维的活跃性都在急剧下降,以后要常常刷题活跃下脑筋了...
最后
以上就是怕孤独煎饼为你收集整理的1722. 执行交换操作后的最小汉明距离(并查集求解连通分量)题目解析的全部内容,希望文章能够帮你解决1722. 执行交换操作后的最小汉明距离(并查集求解连通分量)题目解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复