2023-05-23:如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等, 那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。 例如,“tars“
2023-05-23:如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等,
那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。
例如,"tars" 和 "rats" 是相似的 (交换 0 与 2 的位置);
"rats" 和 "arts" 也是相似的,但是 "star" 不与 "tars","rats",或 "arts" 相似。
总之,它们通过相似性形成了两个关联组:{"tars", "rats", "arts"} 和 {"star"}。
注意,"tars" 和 "arts" 是在同一组中,即使它们并不相似。
形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。
给你一个字符串列表 strs。列表中的每个字符串都是 strs 中其它所有字符串的一个字母异位词。
请问 strs 中有多少个相似字符串组?
输入:strs = ["tars","rats","arts","star"]。
输出:2。
答案2023-05-23:
具体过程如下:
1.定义一个结构体 UnionFind
,包含以下字段:
Father []int
:每个元素的父节点;Size []int
:每个子集的大小;Help []int
:帮助数组;Sets int
:集合数量。
2.编写函数 NewUnionFind(n int) *UnionFind
,创建一个新的并查集,需传入元素数量 n
,实现如下:
创建一个
UnionFind
结构体uf
,分别用make
函数初始化父节点数组、子集大小数组和帮助数组,将集合数量Sets
初始化为元素数量n
;遍历每个元素,将其父节点初始化为自身,子集大小初始化为1。
返回
uf
。
3.编写函数 Find(i int) int
实现路径压缩的查找操作,返回元素 i
所在集合的根节点,具体步骤如下:
定义辅助变量
hi
为0;如果元素
i
的父节点不是它本身,将i
加入帮助数组,将i
更新为其父节点;当
i
的父节点等于它本身时,表明已经到达集合的根节点,遍历帮助数组,依次将这些元素的父节点更新为根节点;返回根节点。
4.编写函数 Union(i, j int)
实现按秩合并的操作,将元素 i
所在集合和元素 j
所在集合合并成一个集合,具体步骤如下:
分别查找元素
i
和元素j
所在集合的根节点,如果它们所在的集合已经相同,则不需要合并;否则,比较两个集合的大小,将小的集合合并到大的集合中,并更新父节点和子集大小,同时将集合数量减1。
5.编写函数 Sets0() int
返回当前并查集中集合的数量,直接返回结构体字段 Sets
的值即可。
6.编写函数 numSimilarGroups(strs []string) int
,遍历每对字符串,如果它们属于不同的集合,判断它们是否相似,如果是相似的则将它们合并到同一个集合中,最终返回并查集中剩余的集合数量,具体步骤如下:
创建一个新的并查集
uf
,元素数量为输入字符串列表strs
的长度;遍历输入字符串列表
strs
,对于每一对字符串s1
和s2
,判断它们是否属于同一个集合,如果不是,则比较它们是否相似,如果是相似的,则将它们所在集合合并;返回并查集中集合的数量。
7.在 main
函数中,给定输入字符串列表 strs
,调用 numSimilarGroups
函数计算相似字符串组的数量,并输出结果。
时间复杂度:在最坏情况下,需要枚举任意两个字符串进行比较,因此需要 $O(n^2m)$ 的时间复杂度,其中 $n$ 是字符串数组 strs 中字符串的数量,$m$ 是字符串的长度。并查集合并操作的时间复杂度为 $\alpha(n)$,其中 $\alpha(n)$ 是反阿克曼函数的某个很小的值,可以看作是常数级别的时间复杂度,因此对总时间复杂度的贡献可以忽略不计。因此,最终的时间复杂度为 $O(n^2m)$。
空间复杂度:主要由并查集所用的空间和额外的辅助变量所占用的空间构成。其中,并查集需要的空间是 $O(n)$,辅助变量 Help 需要的空间也是 $O(n)$,因此总的空间复杂度为 $O(n)$。
go语言完整代码如下:
package main
import "fmt"
func numSimilarGroups(strs []string) int {
n, m := len(strs), len(strs[0])
uf := NewUnionFind(n)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
if uf.Find(i) != uf.Find(j) {
diff := 0
for k := 0; k < m && diff < 3; k++ {
if strs[i][k] != strs[j][k] {
diff++
}
}
if diff == 0 || diff == 2 {
uf.Union(i, j)
}
}
}
}
return uf.Sets0()
}
type UnionFind struct {
Father []int
Size []int
Sets int
Help []int
}
func NewUnionFind(n int) *UnionFind {
uf := &UnionFind{
Father: make([]int, n),
Size: make([]int, n),
Help: make([]int, n),
Sets: n,
}
for i := 0; i < n; i++ {
uf.Father[i] = i
uf.Size[i] = 1
}
return uf
}
func (uf *UnionFind) Find(i int) int {
hi := 0
for i != uf.Father[i] {
uf.Help[hi] = i
hi++
i = uf.Father[i]
}
for hi > 0 {
hi--
uf.Father[uf.Help[hi]] = i
}
return i
}
func (uf *UnionFind) Union(i, j int) {
fi, fj := uf.Find(i), uf.Find(j)
if fi != fj {
if uf.Size[fi] >= uf.Size[fj] {
uf.Father[fj] = fi
uf.Size[fi] += uf.Size[fj]
} else {
uf.Father[fi] = fj
uf.Size[fj] += uf.Size[fi]
}
uf.Sets--
}
}
func (uf *UnionFind) Sets0() int {
return uf.Sets
}
func main() {
strs := []string{"tars", "rats", "arts", "star"}
res := numSimilarGroups(strs)
fmt.Println(res)
}
rust完整代码如下:
fn main() {
let strs = vec![
"tars".to_string(),
"rats".to_string(),
"arts".to_string(),
"star".to_string(),
];
let res = num_similar_groups(strs);
println!("{}", res);
}
fn num_similar_groups(strs: Vec<String>) -> i32 {
let n = strs.len();
let m = strs[0].len();
let mut uf = UnionFind::new(n);
for i in 0..n {
for j in i + 1..n {
// [i] [j]
if uf.find(i) != uf.find(j) {
let mut diff = 0;
for k in 0..m {
if strs[i].as_bytes()[k] != strs[j].as_bytes()[k] {
diff += 1;
}
if diff >= 3 {
break;
}
}
if diff == 0 || diff == 2 {
uf.union(i, j);
}
}
}
}
uf.sets() as i32
}
struct UnionFind {
father: Vec<usize>,
size: Vec<i32>,
help: Vec<usize>, // 添加help字段
sets: usize,
}
impl UnionFind {
fn new(n: usize) -> Self {
let mut father = vec![0; n];
let size = vec![1; n];
for i in 0..n {
father[i] = i;
}
Self {
father,
size,
help: vec![0; n], // 初始化help
sets: n,
}
}
fn find(&mut self, i: usize) -> usize {
let mut hi = 0;
let mut j = i;
while j != self.father[j] {
self.help[hi] = j;
hi += 1;
j = self.father[j];
}
while hi > 0 {
hi -= 1;
self.father[self.help[hi]] = j;
}
j
}
fn union(&mut self, i: usize, j: usize) {
let fi = self.find(i);
let fj = self.find(j);
if fi != fj {
if self.size[fi] >= self.size[fj] {
self.father[fj] = fi;
self.size[fi] += self.size[fj];
} else {
self.father[fi] = fj;
self.size[fj] += self.size[fi];
}
self.sets -= 1;
}
}
fn sets(&self) -> usize {
self.sets
}
}
c语言完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int* father;
int* size;
int* help;
int sets;
} UnionFind;
UnionFind* newUnionFind(int n) {
UnionFind* uf = (UnionFind*)malloc(sizeof(UnionFind));
uf->father = (int*)malloc(sizeof(int) * n);
uf->size = (int*)malloc(sizeof(int) * n);
uf->help = (int*)malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) {
uf->father[i] = i;
uf->size[i] = 1;
}
uf->sets = n;
return uf;
}
int find(UnionFind* uf, int i) {
int hi = 0;
while (i != uf->father[i]) {
uf->help[hi++] = i;
i = uf->father[i];
}
while (hi != 0) {
hi--;
uf->father[uf->help[hi]] = i;
}
return i;
}
void unionSet(UnionFind* uf, int i, int j) {
int fi = find(uf, i);
int fj = find(uf, j);
if (fi != fj) {
if (uf->size[fi] >= uf->size[fj]) {
uf->father[fj] = fi;
uf->size[fi] += uf->size[fj];
}
else {
uf->father[fi] = fj;
uf->size[fj] += uf->size[fi];
}
uf->sets--;
}
}
int getSets(UnionFind* uf) {
return uf->sets;
}
int numSimilarGroups(char** strs, int strsSize) {
int n = strsSize, m = strlen(strs[0]);
UnionFind* uf = newUnionFind(n);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (find(uf, i) != find(uf, j)) {
int diff = 0;
for (int k = 0; k < m && diff < 3; k++) {
if (strs[i][k] != strs[j][k]) {
diff++;
}
}
if (diff == 0 || diff == 2) {
unionSet(uf, i, j);
}
}
}
}
return getSets(uf);
}
int main() {
char* strs[] = { "tars", "rats", "arts", "star" };
int strsSize = sizeof(strs) / sizeof(strs[0]);
int res = numSimilarGroups(strs, strsSize);
printf("%d\n", res); // 输出 2
return 0;
}
c++完整代码如下:
#include <iostream>
#include <vector>
using namespace std;
class UnionFind {
public:
vector<int> father;
vector<int> size;
vector<int> help;
int sets;
UnionFind(int n) : father(n), size(n, 1), help(n), sets(n) {
for (int i = 0; i < n; i++) {
father[i] = i;
}
}
int find(int i) {
int hi = 0;
while (i != father[i]) {
help[hi++] = i;
i = father[i];
}
while (hi != 0) {
father[help[--hi]] = i;
}
return i;
}
void unionSet(int i, int j) {
int fi = find(i);
int fj = find(j);
if (fi != fj) {
if (size[fi] >= size[fj]) {
father[fj] = fi;
size[fi] += size[fj];
}
else {
father[fi] = fj;
size[fj] += size[fi];
}
sets--;
}
}
int getSets() {
return sets;
}
};
int numSimilarGroups(vector<string>& strs) {
int n = strs.size(), m = strs[0].size();
UnionFind uf(n);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (uf.find(i) != uf.find(j)) {
int diff = 0;
for (int k = 0; k < m && diff < 3; k++) {
if (strs[i][k] != strs[j][k]) {
diff++;
}
}
if (diff == 0 || diff == 2) {
uf.unionSet(i, j);
}
}
}
}
return uf.getSets();
}
int main() {
vector<string> strs = { "tars", "rats", "arts", "star" };
int res = numSimilarGroups(strs);
cout << res << endl;
return 0;
}
2023-05-23:如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等, 那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。 例如,“tars“的更多相关文章
- C语言:将ss所指字符串中所有下标为奇数位置的字母转换为大写-将该字符串中的所有字符按ASCII码值升序排序后输出。-将a所指的4*3矩阵第k行的元素与第0行元素交换。
//函数fun:将ss所指字符串中所有下标为奇数位置的字母转换为大写,若不是字母,则不转换. #include<conio.h> #include<stdio.h> #incl ...
- 字符串s中从第i个位置起取长度为len的子串,函数返回子串链表
/*已知字符串采用带结点的链式存储结构(详见linksrting.h文件),请编写函数linkstring substring(linkstring s,int i,int len),在字符串s中从第 ...
- C#中使用IndexOf()判断字符串在字符串数组中第一次出现的索引位置
] {"}; "; //判断字符串的前几位在另一个字符串数组中第一次出现的索引位置 index = Array.IndexOf(s, s1.Substring(, ));
- 给定两个字符串 s 和 t,它们只包含小写字母。 字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。 请找出在 t 中被添加的字母。
给定两个字符串 s 和 t,它们只包含小写字母.字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母.请找出在 t 中被添加的字母. 示例: 输入: s = "abcd" ...
- 字符串数组 输入3个字符串,要求按由小到大的字母顺序输出; 输入n个学生的姓名和学号到字符串数组中,在输入一个姓名,如果班级有该生则返回其信息,否则返回本班无此人
输入3个字符串,要求按由小到大的字母顺序输出 如 输入franch england china,输出结果是china england franch 三个数排序输出,比较三个数的大小怎么做? a=18 ...
- Javascript ----字符串(String)中的方法
涉及字符串时,常用到的几个方法... --------------------------------------------------------------------------------- ...
- c# 获取字符串数组中最长的的字符串并输出最长的字符串
求字符串数组中最大长度的字符串: 实质就是比较字符串的长度: 方案一: class Program { static void Main(string[] args) { string[] array ...
- VBA中字符串连接/字符串拼接中“&”和“+”的区别
VBA中字符串连接/字符串拼接中“&”和“+”的区别 在VBA中用于字符串连接的只有“&”和“+”两种运算符. 1.“&”是强制性连接,就是不管什么都连接. 2.“+”是对 ...
- 字符串匹配算法(在字符串T中查找是否有与字符串P相同的子串)
T称为目标串(Target)或主串 ,P称为模式串(Pattren) 或子串 1. 简单字符串模式匹配算法 原理:用字符串P的字符依次与字符串T中的字符进行比较,首先将字符串P从第0个位置起与主串T的 ...
- Js中的字符串/数组中常用的操作
JS为每种数据类型都内置很多方法,真的不好记忆,而且有些还容易记混,现整理如下,以便以后查看: 一.String ①charAt()方法用于返回指定索引处的字符.返回的字符是长度为 1 的字符串. 语 ...
随机推荐
- SpringBoot笔记--文件配置加载顺序+整合其他框架
内部文件配置加载顺序 外部文件配置加载顺序 jar包配置 整合Junit 若是业务管理类和测试类在同一个包下面,那么这句话, 可以不加括号,只写注解名称 否则,就必须指定到包下面,不然会报错 整合Re ...
- Jetson Xavier NX 试玩 (二)
Jetson Xavier NX 试玩 (二) Hello AI World Inference 人工智能推理模型 0 前言 想玩一玩 jetson 的人工智能功能,官方的 instructional ...
- Django-5
Django-5 1.Coookie 1.1 什么是cookie Cookie是储存在浏览器端的一小段文本数据(键值对). 被广泛用于在网站之间传输信息, 当您访问一个网站时,它会将一个Cookie发 ...
- while与do-while的区别是什么,怎么用?
前言 在上一篇文章中,壹哥给大家讲解了循环的概念,并重点给大家讲解了for循环的使用.但在Java中,除了for循环之外,还有while.do-while.foreach等循环形式.今天壹哥就再用一篇 ...
- CAS 单点登录系统
一.什么是单点登录 单点登录(Sign Sion On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系 ...
- Java面试——Netty
一.BIO.NIO 和 AIO [1]阻塞 IO(Blocking I/O):同步阻塞I/O模式,当一条线程执行 read() 或者 write() 方法时,这条线程会一直阻塞直到读取一些数据或者写出 ...
- JS一切皆对象理解
对象都是通过函数创建的 function Fn() { this.name = '王福朋'; this.year = 1988; } var fn1 = new Fn(); fn1是个对象,它是由函数 ...
- mysql的concat与concat_ws拼接字符串的使用
concat的使用 可以拼接多个字符 mysql> select concat(name,dept,job) from t1; +-----------------------+ | conca ...
- Binder机制及底层实现
<1>进程间的内存空间是进程私有的<2>进程间和内核的空间是互通的<3>进程1空间<--->内核空间<-->进程2空间Binder跨进程通信 ...
- React框架使用
一:使用Vite创建React项目 二:React中组件使用 import React, { Component, useState } from "react"; //使用cla ...