lintcode阶梯训练第五关(九章)

时间:2022-06-15 16:24:24

10、字符串的不同排列

  • 题目
    给出一个字符串,找到它的所有排列,注意同一个字符串不要打印两次。
    样例
    给出 “abb”,返回 [“abb”, “bab”, “bba”]。
    给出 “aabb”,返回 [“aabb”, “abab”, “baba”, “bbaa”, “abba”, “baab”]。

  • 代码

public class Solution {
    public List<String> stringPermutation2(String str) {
        List<String> results = new ArrayList<>();
        if (str == null) {
            return results;
        }
        ArrayList<Character> list = new ArrayList<>();
        char[] s = str.toCharArray();
        Arrays.sort(s);
        boolean[] flag = new boolean[s.length];
        dfsHelper(s, flag, list, results);
        return results;
    }
    private void dfsHelper(char[] s,
                           boolean[] flag,
                           ArrayList<Character> list,
                           List<String> results) {
        if (list.size() == s.length) {
            results.add(toStr(list));
        }
        for (int i = 0; i < s.length; i++) {
            if (flag[i]) {
                continue;
            }
            if (i != 0 && s[i] == s[i - 1] && !flag[i - 1]) {
                continue;
            }
            list.add(s[i]);
            flag[i] = true;
            dfsHelper(s, flag, list, results);
            flag[i] = false;
            list.remove(list.size() - 1);
        }
    }
    private String toStr(ArrayList<Character> list) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < list.size(); i++) {
            sb.append(list.get(i));
        }
        return sb.toString();
    }
}

135、数字组合

  • 题目
    给出一组候选数字(C)和目标数字(T),找到C中所有的组合,使找出的数字和为T。C中的数字可以无限制重复被选取。
    例如,给出候选数组[2,3,6,7]和目标数字7,所求的解为:
    [7],
    [2,2,3]
    注意事项
    所有的数字(包括目标数字)均为正整数。
    元素组合(a1, a2, … , ak)必须是非降序(ie, a1 ≤ a2 ≤ … ≤ ak)。
    解集不能包含重复的组合。
    样例
    给出候选数组[2,3,6,7]和目标数字7
    返回 [[7],[2,2,3]]

  • 代码

public class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (candidates == null || candidates.length == 0) {
            return result;
        }
        Arrays.sort(candidates);
        List<Integer> list = new ArrayList<>();
        dfsHelper(candidates, 0, 0, target, list, result);
        return result;
    }
    private void dfsHelper(int[] candidates,
                           int pos,
                           int sum,
                           int target,
                           List<Integer> list,
                           List<List<Integer>> result) {
        if (sum == target) {
            result.add(new ArrayList<Integer>(list));
        }
        for (int i = pos; i < candidates.length; i++) {
            if (i != pos && candidates[i] == candidates[i - 1]) {
                continue;
            }
            if (candidates[i] + sum > target) {
                break;
            }
            list.add(candidates[i]);
            dfsHelper(candidates, i, candidates[i] + sum, target, list, result);
            list.remove(list.size() - 1);
        }
    }
}

153、数字组合 II

  • 题目
    给出一组候选数字(C)和目标数字(T),找出C中所有的组合,使组合中数字的和为T。C中每个数字在每个组合中只能使用一次。
    注意事项
    所有的数字(包括目标数字)均为正整数。
    元素组合(a1, a2, … , ak)必须是非降序(ie, a1 ≤ a2 ≤ … ≤ ak)。
    解集不能包含重复的组合。
    样例
    给出一个例子,候选数字集合为[10,1,6,7,2,1,5] 和目标数字 8 ,
    解集为:[[1,7],[1,2,5],[2,6],[1,1,6]]

  • 代码

public class Solution {
    public List<List<Integer>> combinationSum2(int[] num, int target) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (num == null || num.length == 0) {
            return result;
        }
        List<Integer> list = new ArrayList<>();
        Arrays.sort(num);
        dfsHelper(num, 0, 0, target, list, result);
        return result;
    }
    private void dfsHelper(int[] num,
                           int pos,
                           int sum,
                           int target,
                           List<Integer> list,
                           List<List<Integer>> result) {
        if (sum == target) {
            result.add(new ArrayList<Integer>(list));
        }
        for (int i = pos; i < num.length; i++) {
            if (i != pos && num[i] == num[i - 1]) {
                continue;
            }
            if (num[i] + sum > target) {
                break;
            }
            list.add(num[i]);
            dfsHelper(num, i + 1, num[i] + sum, target, list, result);
            list.remove(list.size() - 1);
        }
    }
}

136、分割回文串

  • 题目
    给定一个字符串s,将s分割成一些子串,使每个子串都是回文串。
    返回s所有可能的回文串分割方案。
    样例
    给出 s = “aab”,返回
    [
    [“aa”, “b”],
    [“a”, “a”, “b”]
    ]

  • 代码

public class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> results = new ArrayList<List<String>>();
        if (s == null || s.length() == 0) {
            return results;
        }
        List<String> list = new ArrayList<String>();
        dfsHelper(s, list, results);
        return results;
    }
    private void dfsHelper(String s,
                           List<String> list,
                           List<List<String>> results) {
        if (s.length() == 0) {
            results.add(new ArrayList<String>(list));
        }
        for (int i = 0; i < s.length(); i++) {
            if (!isPalind(s, i)) {
                continue;
            }
            list.add(s.substring(0, i + 1));
            dfsHelper(s.substring(i + 1), list, results);
            list.remove(list.size() - 1);
        }
    }
    private boolean isPalind(String s, int i) {
        int start = 0;
        int end = i;
        while (start < end) {
            if (s.charAt(start++) != s.charAt(end--)) {
                return false;
            }
        }
        return true;
    }
}

108、分割回文串 II

  • 题目
    给定一个字符串s,将s分割成一些子串,使每个子串都是回文。
    返回s符合要求的的最少分割次数。
    【需要再来遍】
  • 样例
    比如,给出字符串s = “aab”,
    返回 1, 因为进行一次分割可以将字符串s分割成[“aa”,”b”]这样两个回文子串

  • 代码

public class Solution {
    public int minCut(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        boolean[][] isPalind = isPalindrome(s);
        int[] f = new int[s.length() + 1];
        f[0] = 0;
        for (int i = 1; i <= s.length(); i++) {
            f[i] = i;
            for (int j = 0; j < i; j++) {
                if (isPalind[j][i - 1]) {
                    f[i] = Math.min(f[i], f[j] + 1);
                }
            }
        }
        return f[s.length()] - 1;
    }
    private boolean[][] isPalindrome(String s) {
        int len = s.length();
        boolean[][] isPalind = new boolean[len][len];
        for (int i = 0; i < len; i++) {
            isPalind[i][i] = true;
        }
        for (int i = 0; i < len - 1; i++) {
            isPalind[i][i + 1] = s.charAt(i) == s.charAt(i + 1);
        }
        for (int length = 2; length < len; length++) {
            for (int start = 0; start + length < len; start++) {
                isPalind[start][start + length] = isPalind[start + 1][start + length - 1] && (s.charAt(start) == s.charAt(start + length));
            }
        }
        return isPalind;
    }
}

211、字符串置换

  • 题目
    给定两个字符串,请设计一个方法来判定其中一个字符串是否为另一个字符串的置换。
    置换的意思是,通过改变顺序可以使得两个字符串相等。
    样例
    “abc” 为 “cba” 的置换。
    “aabc” 不是 “abcc” 的置换。

  • 代码

public class Solution {
    public boolean stringPermutation(String A, String B) {
        if (A.length() != B.length()) {
            return false;
        }
        int[] flag = new int[1000];
        int len = A.length();
        for (int i = 0; i < len; i++) {
            flag[A.charAt(i)]++;
        }
        for (int i = 0; i < len; i++) {
            flag[B.charAt(i)]--;
        }
        for (int i = 0; i < len; i++) {
            if (flag[A.charAt(i)] != 0) {
                return false;
            }
        }
        return true;
    }
}

51、上一个排列

  • 题目
    给定一个整数数组来表示排列,找出其上一个排列。
    注意事项
    排列中可能包含重复的整数
    样例
    给出排列[1,3,2,3],其上一个排列是[1,2,3,3]
    给出排列[1,2,3,4],其上一个排列是[4,3,2,1]

  • 代码

public class Solution {
    public ArrayList<Integer> previousPermuation(ArrayList<Integer> nums) {
        int n = nums.size();
        int i = n - 1;
        while (i != 0 && nums.get(i) >= nums.get(i - 1)) {
            i--;
        }
        if (i == 0) {
            reverse(nums, 0, n - 1);
            return nums;
        }
        reverse(nums, i, n - 1);
        int j = i;
        for (; j < n - 1; j++) {
            if (nums.get(j) < nums.get(i - 1)) {
                break;
            }
        }
        swap(nums, i - 1, j);
        return nums;
    }
    private void swap(ArrayList<Integer> nums, int i, int j) {
        int temp = nums.get(i);
        nums.set(i, nums.get(j));
        nums.set(j, temp);
    }
    private void reverse(ArrayList<Integer> nums, int start, int end) {
        for (; start < end; start++, end--) {
            swap(nums, start, end);
        }
    }
}

52、下一个排列

  • 题目
    给定一个整数数组来表示排列,找出其之后的一个排列。
    注意事项
    排列中可能包含重复的整数
    样例
    给出排列[1,3,2,3],其下一个排列是[1,3,3,2]
    给出排列[4,3,2,1],其下一个排列是[1,2,3,4]

  • 代码

public class Solution {
    public int[] nextPermutation(int[] nums) {
        if (nums == null) {
            return nums;
        }
        int n = nums.length;
        int i = n - 1;
        while (i != 0 && nums[i] <= nums[i - 1]) {
            i--;
        }
        if (i == 0) {
            reverse(nums, i, n - 1);
            return nums;
        }
        int j = n - 1;
        for (; j > i - 1; j--) {
            if (nums[j] > nums[i - 1]) {
                break;
            }
        }
        swap(nums, i - 1 , j);
        reverse(nums, i, n - 1);
        return nums;
    }
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    private void reverse(int[] nums, int start, int end) {
        for (; start < end; start++, end--) {
            swap(nums, start, end);
        }
    }
}

190、下一个排列

  • 题目
    给定一个若干整数的排列,给出按正数大小进行字典序从小到大排序后的下一个排列。
    如果没有下一个排列,则输出字典序最小的序列。
    样例
    左边是原始排列,右边是对应的下一个排列。
    1,2,3 → 1,3,2
    3,2,1 → 1,2,3
    1,1,5 → 1,5,1

  • 代码

public class Solution {
    public void nextPermutation(int[] nums) {
        if (nums == null) {
            return;
        }
        int n = nums.length;
        int i = n - 1;
        while (i != 0 && nums[i] <= nums[i - 1]) {
            i--;
        }
        if (i == 0) {
            reverse(nums, 0, n - 1);
            return;
        }
        int j = n - 1;
        for (; j > i - 1; j--) {
            if (nums[j] > nums[i - 1]) {
                break;
            }
        }
        swap(nums, i - 1, j);
        reverse(nums, i, n - 1);
        return;
    }
    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
    private void reverse(int[] nums, int start, int end) {
        for(; start < end; start++, end--) {
            swap(nums, start, end);
        }
    }
}

197、排列序号

  • 题目
    给出一个不含重复数字的排列,求这些数字的所有排列按字典序排序后该排列的编号。其中,编号从1开始。
    样例
    例如,排列 [1,2,4] 是第 1 个排列。

  • 代码

public class Solution {
    public long permutationIndex(int[] A) {
        int[] flag = new int[A.length];
        for (int i = 0; i < A.length; i++) {
            for (int j = i + 1; j < A.length; j++) {
                if (A[i] > A[j]) {
                    flag[i]++;
                }
            }
        }
        long result = 0;
        for (int i = 0; i < A.length; i++) {
            result += flag[i] * Jec(A.length - i - 1);
        }
        return result + 1;
    }
    private long Jec(int x) {
        long ans = 1;
        while (x > 0) {
            ans *= (long) x;
            x--;
        }
        return ans;
    }
}

198、排列序号II

  • 题目
    给出一个可能包含重复数字的排列,求这些数字的所有排列按字典序排序后该排列在其中的编号。编号从1开始。
    【需要再来一遍~】
    样例
    给出排列[1, 4, 2, 2],其编号为3。

  • 代码

public class Solution {
    public long permutationIndexII(int[] A) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < A.length; i++) {
            if (map.containsKey(A[i])) {
                map.put(A[i], map.get(A[i]) + 1);
            } else {
                map.put(A[i], 1);
            }
        }
        long result = 0;
        for (int i = 0; i < A.length; i++) {
            Map<Integer, Integer> flag = new HashMap<>();
            for (int j = i + 1; j < A.length; j++) {
                if (A[i] > A[j] && !flag.containsKey(A[j])) {
                    flag.put(A[j], 1);
                    map.put(A[j], map.get(A[j]) - 1);
                    result += getNum(map);
                    map.put(A[j], map.get(A[j]) + 1);
                }
            }
            map.put(A[i], map.get(A[i]) - 1);
        }
        return result + 1;
    }
    private long getNum(Map<Integer, Integer> map) {
        int sum = 0;
        long dup = 1;
        for (Integer v : map.values()) {
            if (v == 0) {
                continue;
            }
            sum += v;
            dup *= Jec(v);
        }
        if (sum == 0) {
            return sum;
        }
        return Jec(sum) / dup;
    }
    private long Jec(int x) {
        long ans = 1;
        while (x > 0) {
            ans *= (long) x;
            x--;
        }
        return ans;
    }
}

107、单词切分

  • 题目
    给出一个字符串s和一个词典,判断字符串s是否可以被空格切分成一个或多个出现在字典中的单词。
    【需要再来一遍~】
    样例
    给出
    s = “lintcode”
    dict = [“lint”,”code”]
    返回 true 因为”lintcode”可以被空格切分成”lint code”

  • 代码

public class Solution {
    public boolean wordBreak(String s, Set<String> dict) {
        if (s == null || s.length() == 0) {
            return true;
        } //?
        if (dict.isEmpty()) {
            return false;
        }
        int n = s.length();
        boolean[] flag = new boolean[n + 1];
        flag[n] = true;
        int maxLen = getMax(dict);
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i + 1; j - i <= maxLen && j <= n; j++) {
                if (!flag[j]) {
                    continue;
                }
                String sub = s.substring(i, j);
                if (dict.contains(sub)) {
                    flag[i] = true;
                }
            }
        }
        return flag[0];
    }
    private int getMax(Set<String> dict) {
        int maxLen = Integer.MIN_VALUE;
        for (String d : dict) {
            maxLen = Math.max(maxLen, d.length());
        }
        return maxLen;
    }
}

没有maxLen的话会超时~