【Lintcode】10. String Permutation II

题目地址:

https://www.lintcode.com/problem/string-permutation-ii/description

给定一个字符串,求其所有字符的全排列,重复的排列只返回一次。

思路是DFS。由于元素有重复,所以如果直接暴力排列的话,会出现同一种排列出现了很多次的情况,所以难点就在于怎样避免重复。避免的重复的方法是,我们人为规定一个条件,满足这个条件的排列,加入最后的结果中,而对于不满足这个条件的排列,及时略过。为了方便起见,我们先将字符串的字符数组排序,使得相同字符紧邻,然后我们认为规定,相同字符的取法永远都是从左向右连续的取。

具体来说,如果字符串含连续的三个 a a a,分别标记为 a 1 a 2 a 3 a_1a_2a_3 a1a2a3,那么对于某个排列来说,我们只允许取 a 1 a_1 a1 a 1 a 2 a_1a_2 a1a2 a 1 a 2 a 3 a_1a_2a_3 a1a2a3,不允许标号大的排列在标号小的之前,也不允许跳着取。也就是说,我们遇到这样的情况,就需要跳过去,即循环到 i i i的时候, a i = a i − 1 a_i=a_{i-1} ai=ai1,但 a i − 1 a_{i-1} ai1没取过,则此次 i i i需要跳过。这样一来,很显然,第一个 a a a只能取 a 1 a_1 a1,否则就会导致取了 a i a_i ai但是 a i − 1 a_{i-1} ai1没取,那么这样的排列就会略过。同时,也不会产生跳着取的情况,一旦跳着取,中间会有个”漏了“的元素没取,也会略过。代码如下:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    /**
     * @param str: A string
     * @return: all permutations
     */
    public List<String> stringPermutation2(String str) {
        // write your code here
        List<String> res = new ArrayList<>();
        
        if (str == null || str.isEmpty()) {
            res.add("");
            return res;
        }
        
        char[] s = str.toCharArray();
        // 为了使得相同字母挨在一起,需要先排列
        Arrays.sort(s);
        
        boolean[] used = new boolean[s.length];
        dfs(s, new StringBuilder(), used, res);
        
        return res;
    }
    // sb是已经取好的字符,used标记sb里取好的字符
    private void dfs(char[] s, StringBuilder sb, boolean[] used, List<String> res) {
    	// 如果取好了所有字符,则加入res
        if (sb.length() == s.length) {
            res.add(sb.toString());
            return;
        }
    	// 接着从第0位开始枚举下一个要加入sb的字符
        for (int i = 0; i < s.length; i++) {
        	// 如果第i位已经取过,则跳过;如果第i位和第i-1位相同,但第i-1位没取过,
        	// 说明要么相同字符已经取到了第一个之后的那个字符,此时要跳过,
        	// 要么存在跳着取的情况,也需要跳过
            if (used[i] || (i > 0 && s[i] == s[i - 1] && !used[i - 1])) {
                continue;
            }
            
            sb.append(s[i]);
            used[i] = true;
            dfs(s, sb, used, res);
            used[i] = false;
            sb.setLegnth(sb.length() - 1);
        }
    }
}

时间复杂度 O ( n n ! ) O(nn!) O(nn!) n n n为字符串长度),空间 O ( n ) O(n) O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值