2023-05-15:对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次,

能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k。

给你两个字母异位词 s1 和 s2 ,返回 s1 和 s2 的相似度 k 的最小值。

输入:s1 = "abc", s2 = "bca"。

输出:2。

答案2023-05-15:

解题思路:

  • 定义一个小根堆,按照节点的估值函数进行排序。

  • 初始化节点为 s1,将其加入小根堆。同时记录访问过的节点,以避免重复搜索。

  • 从小根堆中弹出代价最小的节点 cur。

  • 如果 cur 与 s2 相等,则返回当前代价 cost。

  • 否则,找到 cur 与 s2 第一个不同的位置 firstDiff,再枚举 firstDiff 之后的位置 i。

  • 如果 cur[i] 与 s2[firstDiff] 相等但不在第 i 个位置,则构造一个新的字符串 newStr,交换 newStr[firstDiff] 和 newStr[i] 的位置。

  • 将 newStr 加入小根堆,代价是 cost+1,where 是 firstDiff+1。

  • 在加入前判断是否已经访问过,如果访问过就跳过该节点。

  • 将 newStr 和 cur 恢复为原始状态(恢复数组)。

  • 重复上述步骤,直到小根堆为空或者找到相同的字符串。

需要注意的点:

  • 估值函数的实现是可以调整的,可以根据实际情况来实现更加合适的估值函数。

  • 在 Go 中没有提供 C 语言中的 strdup 函数。可以使用 string 转换为字节数组 []byte,然后再转换为字符串。

  • 在 Go 中 map 是无序的,如果想要按照访问顺序遍历可以在 Node 中增加一个 visited 字段,每次入队时设置 visited = true,在出队时判断 visited 是否为 true,如果为 true 则跳过。

时间复杂度为O(n^2),其中n是字符串的长度。

空间复杂度为O(n^2),存储小根堆和visited哈希表所需的空间。

go完整代码如下:

package main

import "fmt"

type Node struct {
cost int
guess int
where_ int
str_ string
} func kSimilarity(s1 string, s2 string) int {
len := len(s1)
heap := &NodeHeap{}
visited := make(map[string]struct{})
heap.Push(&Node{0, 0, 0, s1})
for heap.Len() > 0 {
cur := heap.Pop().(*Node)
if _, ok := visited[cur.str_]; ok {
continue
}
if cur.str_ == s2 {
return cur.cost
}
visited[cur.str_] = struct{}{}
firstDiff := cur.where_
for firstDiff < len && cur.str_[firstDiff] == s2[firstDiff] {
firstDiff++
}
curStr := []byte(cur.str_)
for i := firstDiff + 1; i < len; i++ {
if curStr[i] == s2[firstDiff] && curStr[i] != s2[i] {
swap(curStr, firstDiff, i)
add(string(curStr), s2, cur.cost+1, firstDiff+1, heap, visited)
swap(curStr, firstDiff, i)
}
}
}
return -1
} type NodeHeap []*Node func (h NodeHeap) Len() int { return len(h) } func (h NodeHeap) Less(i, j int) bool {
return h[i].cost+h[i].guess < h[j].cost+h[j].guess
} func (h NodeHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *NodeHeap) Push(x interface{}) {
*h = append(*h, x.(*Node))
} func (h *NodeHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
} func add(add string, s2 string, cost int, index int, heap *NodeHeap, visited map[string]struct{}) {
if _, ok := visited[add]; !ok {
heap.Push(&Node{cost, evaluate(add, s2, index), index, add})
}
} func swap(s []byte, i int, j int) {
tmp := s[i]
s[i] = s[j]
s[j] = tmp
} func evaluate(s1 string, s2 string, index int) int {
diff := 0
for i := index; i < len(s1); i++ {
if s1[i] != s2[i] {
diff++
}
}
return (diff + 1) / 2
} func main() {
s1 := "abc"
s2 := "bca"
fmt.Println(kSimilarity(s1, s2))
s1 = "ab"
s2 = "ba"
fmt.Println(kSimilarity(s1, s2))
}

rust完整代码如下:

#[derive(Eq, PartialEq)]
struct Node {
cost: i32,
guess: i32,
where_: usize,
str_: String,
} impl Ord for Node {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.cost + self.guess).cmp(&(other.cost + other.guess))
}
} impl PartialOrd for Node {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
} fn k_similarity(s1: String, s2: String) -> i32 {
let len = s1.len();
let mut heap = std::collections::BinaryHeap::new();
let mut visited = std::collections::HashSet::<String>::new();
heap.push(Node {
cost: 0,
guess: 0,
where_: 0,
str_: s1,
});
while let Some(cur) = heap.pop() {
if visited.contains(&cur.str_) {
continue;
}
if cur.str_ == s2 {
return cur.cost;
}
visited.insert(cur.str_.clone());
let mut first_diff = cur.where_;
while cur.str_.chars().nth(first_diff).unwrap() == s2.chars().nth(first_diff).unwrap() {
first_diff += 1;
}
let cur_str = cur.str_.as_bytes();
for i in first_diff + 1..len {
if cur_str.get(i).unwrap() == &s2.as_bytes()[first_diff]
&& cur_str.get(i).unwrap() != &s2.as_bytes()[i]
{
let mut new_str = String::from_utf8_lossy(cur_str).to_string();
swap(&mut new_str, first_diff, i);
add(
new_str,
s2.clone(),
cur.cost + 1,
first_diff + 1,
&mut heap,
&visited,
);
}
}
}
-1
} fn swap(s: &mut String, i: usize, j: usize) {
unsafe {
let s_mut = s.as_mut_vec();
let tmp = std::ptr::read(s_mut.get_unchecked(i));
std::ptr::copy_nonoverlapping(s_mut.get_unchecked(j), s_mut.get_unchecked_mut(i), 1);
std::ptr::write(s_mut.get_unchecked_mut(j), tmp);
}
} fn add(
add: String,
s2: String,
cost: i32,
index: usize,
heap: &mut std::collections::BinaryHeap<Node>,
visited: &std::collections::HashSet<String>,
) {
if !visited.contains(&add) {
heap.push(Node {
cost,
guess: evaluate(&add, &s2, index),
where_: index,
str_: add,
});
}
} // 估值函数
// 看每周有营养的大厂算法面试题,2022年1月第3周
// 估值函数的估计值要绝对 <= 真实距离
// 但又要确保估计值足够大足够接近真实距离,这样效果最好
fn evaluate(s1: &str, s2: &str, index: usize) -> i32 {
let mut diff = 0;
for i in index..s1.len() {
if s1.chars().nth(i).unwrap() != s2.chars().nth(i).unwrap() {
diff += 1;
}
}
(diff + 1) as i32 / 2
} fn main() {
let s1 = String::from("abc");
let s2 = String::from("bca");
println!("{}", k_similarity(s1, s2));
let s1 = String::from("ab");
let s2 = String::from("ba");
println!("{}", k_similarity(s1, s2));
}

c完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef struct Node {
int cost; // 代价,已经换了几回了!
int guess;// 猜测还要换几回,能变对!
int where;// 有必须去比对的下标,左边不再换了!
char* str; // 当前的字符
} Node; int CompareNode(const void* a, const void* b) {
Node* pa = (Node*)a;
Node* pb = (Node*)b;
return (pa->cost + pa->guess) - (pb->cost + pb->guess);
} void swap(char* a, char* b) {
char tmp = *a;
*a = *b;
*b = tmp;
} int evaluate(char* s1, char* s2, int index, int len) {
int diff = 0;
for (int i = index; i < len; i++) {
if (s1[i] != s2[i]) {
diff++;
}
}
return (diff + 1) / 2;
} void add(char* add, char* s2, int cost, int index, Node* heap, int heapSize,
char** visited, int* visitedSize) {
for (int i = 0; i < *visitedSize; i++) { // 判断是否已经访问过
if (strcmp(add, visited[i]) == 0) {
return;
}
}
Node next;
next.cost = cost;
next.guess = evaluate(add, s2, index, strlen(add));
next.where = index;
next.str = _strdup(add);
heap[heapSize] = next;
qsort(heap, heapSize + 1, sizeof(Node), CompareNode); // 排序小根堆
visited[*visitedSize] = _strdup(add);
(*visitedSize)++;
} int kSimilarity(char* s1, char* s2) {
int len = strlen(s1);
Node* heap = (Node*)malloc(sizeof(Node) * len * len); // 分配空间
int heapSize = 0;
char** visited = (char**)malloc(sizeof(char*) * len * len); // 分配空间
int visitedSize = 0;
add(s1, s2, 0, 0, heap, heapSize++, visited, &visitedSize); while (heapSize > 0) {
Node cur = heap[0];
Node tmp = heap[--heapSize]; // 最后一个节点移到根节点,并下移
heap[0] = tmp;
int index = 0;
while (index * 2 + 1 < heapSize) {
int left = index * 2 + 1;
int right = index * 2 + 2;
int min = left;
if (right < heapSize && (heap[right].cost + heap[right].guess) < (heap[left].cost + heap[left].guess)) {
min = right;
}
if ((heap[index].cost + heap[index].guess) > (heap[min].cost + heap[min].guess)) {
tmp = heap[index];
heap[index] = heap[min];
heap[min] = tmp;
index = min;
}
else {
break;
}
}
if (strcmp(cur.str, s2) == 0) {
int cost = cur.cost;
free(cur.str);
for (int i = 0; i < heapSize; i++) {
free(heap[i].str);
}
free(heap); // 释放内存
for (int i = 0; i < visitedSize; i++) {
free(visited[i]);
}
free(visited);
return cost;
}
int firstDiff = cur.where;
while (cur.str[firstDiff] == s2[firstDiff]) {
firstDiff++;
}
for (int i = firstDiff + 1; i < len; i++) {
if (cur.str[i] == s2[firstDiff] && cur.str[i] != s2[i]) {
char* newStr = _strdup(cur.str); // 复制字符串
swap(&newStr[firstDiff], &newStr[i]); // 交换字符
add(newStr, s2, cur.cost + 1, firstDiff + 1, heap, heapSize++, visited, &visitedSize); // 加入新节点
swap(&newStr[firstDiff], &newStr[i]); // 恢复字符串
}
}
free(cur.str);
} for (int i = 0; i < heapSize; i++) {
free(heap[i].str);
}
free(heap); // 释放内存
for (int i = 0; i < visitedSize; i++) {
free(visited[i]);
}
free(visited); return -1;
} int main() {
char s1[] = "abc";
char s2[] = "bca";
printf("%d\n", kSimilarity(s1, s2));
char s3[] = "ab";
char s4[] = "ba";
printf("%d\n", kSimilarity(s3, s4));
return 0;
}

c++完整代码如下:

#include <iostream>
#include <string>
#include <queue>
#include <unordered_set> using namespace std; struct Node {
int cost; // 代价,已经换了几回了!
int guess;// 猜测还要换几回,能变对!
int where;// 有必须去比对的下标,左边不再换了!
string str; // 当前的字符 Node(int r, int g, int i, string s) : cost(r), guess(g), where(i), str(s) {}
}; struct CompareNode {
bool operator()(const Node& a, const Node& b) {
return (a.cost + a.guess) > (b.cost + b.guess); // 小根堆
}
}; void swap(char& a, char& b) {
char tmp = a;
a = b;
b = tmp;
} int evaluate(string s1, string s2, int index) {
int diff = 0;
for (int i = index; i < s1.size(); i++) {
if (s1[i] != s2[i]) {
diff++;
}
}
return (diff + 1) / 2;
} void add(string add, string s2, int cost, int index, priority_queue<Node, vector<Node>, CompareNode>& heap,
unordered_set<string>& visited) {
if (!visited.count(add)) {
heap.push(Node(cost, evaluate(add, s2, index), index, add));
}
} int kSimilarity(string s1, string s2) {
int len = s1.size();
priority_queue<Node, vector<Node>, CompareNode> heap;
unordered_set<string> visited;
heap.push(Node(0, 0, 0, s1));
while (!heap.empty()) {
Node cur = heap.top();
heap.pop();
if (visited.count(cur.str)) {
continue;
}
if (cur.str == s2) {
return cur.cost;
}
visited.insert(cur.str);
int firstDiff = cur.where;
while (cur.str[firstDiff] == s2[firstDiff]) {
firstDiff++;
}
string curStr = cur.str;
for (int i = firstDiff + 1; i < len; i++) {
if (curStr[i] == s2[firstDiff] && curStr[i] != s2[i]) {
swap(curStr[firstDiff], curStr[i]);
add(curStr, s2, cur.cost + 1, firstDiff + 1, heap, visited);
swap(curStr[firstDiff], curStr[i]);
}
}
}
return -1;
} int main() {
string s1 = "abc";
string s2 = "bca";
cout << kSimilarity(s1, s2) << endl;
s1 = "ab";
s2 = "ba";
cout << kSimilarity(s1, s2) << endl;
}

2023-05-15:对于某些非负整数 k ,如果交换 s1 中两个字母的位置恰好 k 次, 能够使结果字符串等于 s2 ,则认为字符串 s1 和 s2 的 相似度为 k。 给你两个字母异位词 s1的更多相关文章

  1. [LeetCode] 854. K-Similar Strings 相似度为K的字符串

    Strings A and B are K-similar (for some non-negative integer K) if we can swap the positions of two ...

  2. [Swift]LeetCode854. 相似度为 K 的字符串 | K-Similar Strings

    Strings A and B are K-similar (for some non-negative integer K) if we can swap the positions of two ...

  3. 《程序员代码面试指南》第七章 位运算 在其他数都出现k 次的数组中找到只出现一次的数

    题目 在其他数都出现k 次的数组中找到只出现一次的数 java 代码 package com.lizhouwei.chapter7; /** * @Description: 在其他数都出现k 次的数组 ...

  4. C语言:将ss所指字符串中所有下标为奇数位置的字母转换为大写-将该字符串中的所有字符按ASCII码值升序排序后输出。-将a所指的4*3矩阵第k行的元素与第0行元素交换。

    //函数fun:将ss所指字符串中所有下标为奇数位置的字母转换为大写,若不是字母,则不转换. #include<conio.h> #include<stdio.h> #incl ...

  5. leetcode 24. 两两交换链表中的节点 及 25. K 个一组翻转链表

    24. 两两交换链表中的节点 问题描述 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例: 给定 1->2-> ...

  6. python实现给定K个字符数组,从这k个字符数组中任意取一个字符串,按顺序拼接,列出所有可能的字符串组合结果!

    题目描述:给定K个字符数组,从这k个字符数组中任意取一个字符串,按顺序拼接,列出所有可能的字符串组合结果! 样例: input:[["a","b"," ...

  7. 【编程题目】输入两个整数 n 和 m,从数列 1,2,3.......n 中 随意取几个数, 使其和等于 m ... ★

    第 21 题(数组)2010 年中兴面试题编程求解:输入两个整数 n 和 m,从数列 1,2,3.......n 中 随意取几个数,使其和等于 m ,要求将其中所有的可能组合列出来. 我的思路: 从小 ...

  8. 交换数组中两个元素的位置,元素包括key和value 一维数组

    /*author: yangyu@sina.cndescription: 交换数组中两个元素的位置,元素包括key和value,具体用法见下面的例子*/$arr = array(11=>'a', ...

  9. 输入两个整数n和m,从数列1,2,3,……n中随意取几个数,使其和等于m

    题目:编程求解,输入两个整数n和m,从数列1,2,3,……n中随意取几个数,使其和等于m.要求将所有的可能组合列出来. 分析:分治的思想.可以把问题(m,n)拆分(m - n, n -1)和(m, n ...

  10. Shell脚本中字符串判空:使用-z 字符串长度为0时,为真,-n字符串长度不为0,为真。这两个都不靠谱【转】

    最近发现使用  -z   和  -n  来判断字符串判空,或不空时,很不靠谱. 使用下面的方法最可靠: if [ "x${value}" == "x" ]    ...

随机推荐

  1. Android笔记--选择按钮

    复选框CheckBox 具体实现: 注意: 开关按钮switch 具体实现: 单选按钮RadioButton 需要放到radioGroup里面写单选按钮 具体实现:

  2. k8s HPA(HorizontalPodAutoscaler)--自动水平伸缩

    写在前面 我们平时部署web服务,当服务压力大撑不住的时候,我们会加机器(加钱):一般没有上容器编排是手动加的,临时加的机器,临时部署的服务还要改Nginx的配置,最后回收机器的时候,也是手动回收,手 ...

  3. dart基础---->dart语法入门

    Dart is an open source, structured programming language for creating complex, browser-based web appl ...

  4. Sokit(TCP/UDP调试工具)

    下载:http://www.winwin7.com/soft/56522.html#xiazai Sokit中文版是一款免费开源的TCP / UDP 测试(调试)工具,它主要可以用于接收和发送TCP/ ...

  5. VUE3.x之Proxy 我们为什么要使用Proxy

    Object.defineProperty 劫持数据 只是对对象的属性进行劫持 无法监听新增属性和删除属性 需要使用 vue.set, vue.delete 深层对象的劫持需要一次性递归 劫持数组时需 ...

  6. R语言文本挖掘细胞词库的转换

    搜狗细胞词库解析 一. 加载R包转换 library(rJava) library(Rwordseg) write.csv(as.data.frame(importSogouScel('wuliu.s ...

  7. flask+gunicorn+nginx部署pytorch/python应用

    1. 基于flask实现python服务Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 .Flask使用 ...

  8. python中文文档

    这是在线中文文档 https://docs.python.org/zh-cn/3.7/library/winreg.html

  9. python和js实现AES加解密

    小白学习中...... AES算法 AES全称为高级加密标准,是Advanced Encryption Standard的首字母简写.详细了解,可以找专门的资料进行学习. 场景 开发一个web网站过程 ...

  10. JAVASE和JAVAEE的区别

    JAVASE和JAVAEE的区别 JavaEE: Java Enterprise Edition,Java企业版,多用于企业级开发,包括web开发等等.企业版本帮助开发和部署可移植.健壮.可伸缩切安全 ...