前言
一题多解,打开格局与视野,Pre-Graph + component-DFS类型,也可使用并查集将两个有相似节点的树进行合并,合并一次,连通分量少一个。
一、案例
二、题解
1)Pre-Graph + component-循环DFS|BFS(朴素处理法,虚节点处理法。) 2)并查集,将两个有相同节点的树合并在一起。
package com.xhu.offer.offerII;
import java.util.*;
public class NumSimilarGroups {
public int numSimilarGroups(String[] strs) {
Set<String> ori = new HashSet<>();
for (String str : strs) ori.add(str);
for (String str : strs) {
if (nodeNum == strs.length) break;
addEdge(str, ori);
}
int count = 0;
int[] visited = new int[nodeNum];
for (String str : strs) {
if (visited[nodes.get(str)] != 1) {
count++;
dfs(str, visited);
}
}
return count;
}
private void dfs(String str, int[] visited) {
int id = nodes.get(str);
visited[nodes.get(str)] = 1;
Set<Integer> original = edges.get(id);
for (Integer other : original) {
if (visited[other] == 0) dfs(reflect.get(other), visited);
}
}
private void addEdge(String str, Set<String> ori) {
addNode(str);
List<String> virtualNodes = produceVirtualNodes(str, ori);
int idx1 = nodes.get(str);
for (String virtualNode : virtualNodes) {
addNode(virtualNode);
int idx2 = nodes.get(virtualNode);
edges.get(idx1).add(idx2);
edges.get(idx2).add(idx1);
}
}
private List<String> produceVirtualNodes(String str, Set<String> ori) {
int len = str.length();
char[] chs = str.toCharArray();
List<String> nodes = new ArrayList<>();
for (int i = 0; i < len - 1; i++) {
char beginCh = chs[i];
for (int j = i + 1; j < len; j++) {
char endCh = chs[j];
chs[i] = endCh;
chs[j] = beginCh;
String s = String.valueOf(chs);
if (ori.contains(s)) nodes.add(s);
chs[i] = beginCh;
chs[j] = endCh;
}
}
return nodes;
}
private void addNode(String str) {
if (!nodes.containsKey(str)) {
nodes.put(str, nodeNum++);
edges.add(new HashSet<>());
reflect.add(str);
}
}
Map<String, Integer> nodes = new HashMap<>();
List<Set<Integer>> edges = new ArrayList<>();
int nodeNum;
List<String> reflect = new ArrayList<>();
}
class NumSimilarGroups2 {
public int numSimilarGroups(String[] strs) {
Set<String> ori = new HashSet<>();
for (String str : strs) ori.add(str);
for (String str : strs) {
addEdge(str, ori);
}
int count = 0;
for (String str : strs) {
if (ori.contains(str)) {
count++;
dfs(str, ori);
}
}
return count;
}
private void dfs(String str, Set<String> ori) {
ori.remove(str);
Set<String> original = graph.get(str);
for (String s : original) {
if (ori.contains(s)) dfs(s, ori);
}
}
private void addEdge(String str, Set<String> ori) {
addNode(str);
Set<String> edge1 = graph.get(str);
for (String node : ori) {
if (similar(str, node)) {
addNode(node);
edge1.add(node);
graph.get(node).add(str);
}
}
}
private boolean similar(String str, String node) {
int cnt = 0, len = str.length();
for (int i = 0; i < len; i++) {
if (str.charAt(i) != node.charAt(i)) cnt++;
if (cnt > 2) return false;
}
return cnt == 2;
}
private void addNode(String str) {
if (!graph.containsKey(str)) graph.put(str, new HashSet<>());
}
Map<String, Set<String>> graph = new HashMap<>();
}
class NumSimilarGroups3 {
public int numSimilarGroups(String[] strs) {
int n = strs.length;
int[] father = new int[n];
for (int i = 0; i < n; i++) father[i] = i;
int count = n;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (similar(strs[i], strs[j]) && union(father, i, j)) {
count--;
}
}
}
return count;
}
private boolean union(int[] father, int i, int j) {
int root1 = findFather(father, i);
int root2 = findFather(father, j);
if (root1 != root2) {
father[root1] = root2;
return true;
}
return false;
}
private int findFather(int[] father, int idx) {
if (idx == father[idx]) return father[idx];
int root = findFather(father, father[idx]);
return root;
}
private int findFather2(int[] father, int idx) {
if (idx != father[idx])
father[idx] = findFather2(father, father[idx]);
return father[idx];
}
private boolean similar(String s, String another) {
int cnt = 0;
int len = s.length();
for (int i = 0; i < len; i++) {
if (s.charAt(i) != another.charAt(i)) ++cnt;
if (cnt > 2) return false;
}
return 1 != cnt;
}
}
总结
1)深入理解并查集的思想,做到其它问题与并查集的转换。 2)一题多解,打开格局与视野。
参考文献
[1] LeetCode 想似的字符串
|