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

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;
}

在这里插入图片描述

posted @ 2023-05-15 22:37  福大大架构师每日一题  阅读(26)  评论(0编辑  收藏  举报