LeetCode 记录-854. 相似度为 K 的字符串

114 阅读2分钟

LeetCode 记录-854. 相似度为 K 的字符串

我的解法

思路

image.png

看到相似度 k 的最小值,脑子里第一个闪出来的做法是用动态规划。但是不知道该怎么去动态规划。


官方解法 1: 广度优先搜索

思路

image.png

这和我所理解的广度优先搜索算法有所不同(我之前一直觉得和树相关的才能用)。在这里,我理解之所以称为 BFS 解法的原因,是用了一个 queue 去遍历所有交换一次、两次、三次...k 次的情况,一直到找到交换 k 次后使得两个字符串相同再停止,这时候的 k 就是最小的交换次数。

代码

var kSimilarity = function (s1, s2) {
  const n = s1.length;
  const queue = [];
  const visit = new Set();
  queue.push([s1, 0]);
  visit.add(s1);
  let step = 0;
  while (queue.length) {
    const sz = queue.length;
    for (let i = 0; i < sz; i++) {
      let [cur, pos] = queue.shift();
      if (cur === s2) {
        return step;
      }
      while (pos < n && cur[pos] === s2[pos]) {
        pos++;
      }
      for (let j = pos + 1; j < n; j++) {
        if (s2[j] === cur[j]) {
          continue;
        }
        if (s2[pos] === cur[j]) {
          const next = swap(cur, pos, j);
          if (!visit.has(next)) {
            visit.add(next);
            queue.push([next, pos + 1]);
          }
        }
      }
    }
    step++;
  }
  return step;
};

const swap = (cur, i, j) => {
  const arr = [...cur];
  const c = arr[i];
  arr[i] = arr[j];
  arr[j] = c;
  return arr.join("");
};

复杂度分析

该方法时空复杂度分析较为复杂,暂不讨论。

官方解法 2: 深度优先搜索

思路

image.png

这个算法相对于 BFS 的区别就是优先搜索同一个子状态下所有交换次数的可能。能体会到这两种算法在非树形结构上的差异。

同时,它用来剪枝的思路也是非常有意思,但是也是比较难想到的。

代码

var kSimilarity = function (s1, s2) {
  let str1 = "";
  let str2 = "";
  for (let i = 0; i < s1.length; i++) {
    if (s1[i] !== s2[i]) {
      str1 += s1[i];
      str2 += s2[i];
    }
  }
  if (str1.length === 0) {
    return 0;
  }
  let ans = str1.length - 1;

  const dfs = (pos, cost, len, str1, str2) => {
    if (cost > ans) {
      return;
    }
    while (pos < str1.length && str1[pos] === str2[pos]) {
      pos++;
    }
    if (pos === str1.length) {
      ans = Math.min(ans, cost);
      return;
    }
    /* 当前状态的交换次数下限大于等于当前的最小交换次数 */
    if (cost + minSwap(str1, str2, pos) >= ans) {
      return;
    }
    for (let i = pos + 1; i < str1.length; i++) {
      if (str1[i] === str2[pos]) {
        const str1Next = swap(str1, i, pos);
        dfs(pos + 1, cost + 1, len, str1Next, str2);
      }
    }
  };

  const minSwap = (s1, s2, pos) => {
    let tot = 0;
    for (let i = pos; i < s1.length; i++) {
      tot += s1[i] !== s2[i] ? 1 : 0;
    }
    return Math.floor((tot + 1) / 2);
  };

  const swap = (cur, i, j) => {
    const arr = [...cur];
    const c = arr[i];
    arr[i] = arr[j];
    arr[j] = c;
    return arr.join("");
  };

  dfs(0, 0, str1.length, str1, str2);
  return ans;
};

复杂度分析

该方法时空复杂度分析较为复杂,暂不讨论。

官方解法 3:动态规划

该解法思维难度较大且时间复杂度较高,就先不看了。