LeetCode 题目854(k相似度字符串)

839 阅读3分钟
  1. 题目链接 leetcode-cn.com/problems/k-…
  2. 题目描述
    1. 如果可以通过将 A 中的两个小写字母精确地交换位置 K 次得到与 B 相等的字符串,我们称字符串 A 和 B 的相似度为 K(K 为非负整数)。 给定两个字母异位词 A 和 B ,返回 A 和 B 的相似度 K 的最小值。  (来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/k-similar-strings 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。)
    2. 限制条件
      1. 1 <= A.length == B.length <= 20 
      2. A 和 B 只包含集合 {'a', 'b', 'c', 'd', 'e', 'f'} 中的小写字母。  
  3. 思路
    1.  一般看到输入的规模比较短,比如10, 20,可以向字符串掩码,动态规划方向思考;
    2. 假如在A,B中相同位置,有相同的字符,那么该位置可以忽略。因为如果要对该位置进行交换,必须也交换来一个相同的字符(要和B[i]一致)。而我们要找到更小的K,所以不交换会得到一个更好的结果。
    3. 我们思考另一个简单的问题,符合什么条件的两个字符串,可以通过交换(不论次数),从A得到B呢?答案是,只要这两个字符串的各个字母的数量是一致的。比如字符串abc 和 bca,进过两次交换,就可以从A变成B。abd和bca,无论怎么交换都无法从A变成B;
    4. 对于上面的例子abc,bca。为什么是交换了两次呢?如果把两个字符串各个位置对应起来,a->b, b -> c, c -> a, 使用 -> 是有意的。我们发现这正好组成了一个环,而环的长度是3,交换的次数是3 - 1。 进而,我们可以得到一个结论,对于一个长度为n的环,交换的次数是n-1;
    5. 回到题目,对于字符串A,B(长度为n),最多交换n-1次,就可以从A变成B。我们要得到最小的交换次数K, 就需要增加更多的环,因为每增加一个,就可以减少一次交换;
    6. 我们把所有的组合可能映射到(1 到 1 << n) 的状态,然后检查该组合是否可以组成一个环。把这样的状态保存下来,并按照状态内1的个数排序(这样的目的是为了在处理后面的状态时,它所依赖的状态已经被正确处理了)。假设对于状态 x, dp[x] 表示该状态的最大环数;dp[y] = max(dp[x] + dp[z], 如果 x | z = y, 并且 x & z = 0);最终的答案是 dp[1 << n - 1]。
    7. 时间复杂度,至少是 n * (2**n), (dp[x]的计算的复杂度我算不出来,^-^) 
  4. 代码
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.util.Sorting
object Solution {  
    def kSimilarity(A: String, B: String): Int = {    
        val as = A.toCharArray    
        val bs = B.toCharArray    
        var n = 0    
        var i = 0    
        while (i < as.length) {      
            if (as(i) != bs(i)) {        
                as(n) = as(i)        
                bs(n) = bs(i)        
                n += 1      
            }      
            i += 1    
        }    
        if (n == 0) {      
            0    
        } else {      
            // n is the length of different letters      
            val N = 1 << n      
            val cnt = Array.ofDim[Int](6)      
            val groupBuf = ArrayBuffer.empty[(Int, Int)]      
            var mask = 1      
            while (mask < N) {        
                (0 until 6).foreach(j => cnt(j) = 0)        
                var l = 0        
                i = 0        
                while (i < n) {          
                    if ((mask & (1 << i)) > 0) {            
                        l += 1            
                        cnt(as(i) - 'a') += 1            
                        cnt(bs(i) - 'a') -= 1          
                    }          
                    i += 1        
                }        
                val can = cnt.forall(_ == 0)        
                if (can) {          
                    // we can group positions in mask as one          
                    groupBuf += mask -> l        
                }        
                mask += 1      
            }     
            val groups = groupBuf.toArray      
            Sorting.quickSort(groups)(Ordering.fromLessThan((a, b) => a._2 < b._2))      
            val ii = mutable.Map.empty[Int, Int].withDefaultValue(-1)      
            (0 until groups.length).foreach(i => ii += groups(i)._1 -> i)      
            val dp = Array.fill(groups.length)(1)      
            i = 0      
            while (i < groups.length) {        
                val cur = groups(i)        
                val mask = cur._1        
                var j = 0        
                while (j < i) {          
                    val tmp = groups(j)          
                    val first = tmp._1          
                    if ((mask & first) == first) {            
                        val second = mask ^ first            
                        val k = ii(second)            
                        if (k >= 0) {              
                            dp(i) = dp(i) max (dp(j) + dp(k))            
                        }          
                    }          
                    j += 1        
                }        
                i += 1      
            }      
            n - dp(groups.length - 1)    
        }  
      }
}