搞定字符串类面试题-Palindrome

409 Longest Palindrome[Easy]

  • 思路: 不能直接用一个hash数组来做记录奇偶数,因为会出现多个总数为奇数的字符和一个总数为奇数的长字符。
  • 还要进行判断建一个HashSet需要挑出最长的奇数字符串 没有将奇数字符串中的成对出现的偶数部分加入到最后的结果中。
class Solution {
    public int longestPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        HashSet<Character> set = new HashSet<>();
        int count = 0;
        for (int i = 0; i < s.length(); i++) {
            if (set.contains(s.charAt(i))) {
                set.remove(s.charAt(i));
                count++;
            } else {
                set.add(s.charAt(i));
            }
        }
        if (set.isEmpty()) {
            return count * 2;
        }
        return count * 2 + 1;
    }
}
复制代码

49. Group Anagrams[Medium]

  • 思路: 用一个hashtable来存 数组来存一个集合? 如何能找到不同字母组合的一致性? 只需要包含相同的字母即可, 顺序没有关系
  • 需要的是一个flag来做记录 两个选择: 1. 类似a1b2c3的结构 2.直接排序得到有序结果
class Solution {
    public List<List<String>> groupAnagrams(String[] strs) {
        if (strs == null || strs.length == 0) {
            return new ArrayList<>();
        }
        List<List<String>> res = new ArrayList<>();
        Map<String, List<String>> map = new HashMap<>();
        int[] count = new int[26];
        
        for (String str : strs) {
            Arrays.fill(count, 0);
            for (char c : str.toCharArray()) {
                count[c - 'a']++;
            }
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 26; i++) {
                if (count[i] != 0) {
                    sb.append((char)(i + 'a')).append(count[i]);
                }
            }
            String flag = sb.toString();
            if (!map.containsKey(flag)) {
                map.put(flag, new ArrayList<>());
            }
            map.get(flag).add(str);
        }
        return new ArrayList<>(map.values());
    }
}
复制代码

131. Palindrome Partitioning[Medium]

  • 题目: 给出一个字符串,找出所有的回文子串并添加到列表当中返回
  • 思路: 动态规划来找出不同的切割方案 dp[i][j] = dp[i - 1][j + 1],要写出来DFS
class Solution {
    public List<List<String>> partition(String s) {
        List<List<String>> res = new ArrayList<>();
        dfs(s, res, new ArrayList<String>(), 0);
        return res;
    }

    private void dfs(String s, List<List<String>> res, List<String> cur, int start) {
        if (s.length() == start) {
            res.add(new ArrayList<>(cur));
            return;
        }
        // backtracking 的添加条件 作为isPalindrome
        for (int i = start + 1; i <= s.length(); i++) {
            if (isPalindrome(s, start, i - 1)) {
                cur.add(s.substring(start, i));
                dfs(s, res, cur, i);
                cur.remove(cur.size() - 1);
            }
        }
    }

    private boolean isPalindrome(String s, int i , int j) {
        while (i < j) {
            if (s.charAt(i) == s.charAt(j)) {
                i++;
                j--;
            } else if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }
}
复制代码

132. Palindrome Partitioning II[Medium]

  • 题目:给出一个字符串str, 返回把str全部切割为回文子串的最小分割数
  • 思路: 想要找到最小分割数,需要使分割次数尽量少,用dp来求得最优解

267. Palindrome PermutationII [Medium]

  • 思路: Permutation 如何找到? DFS + backtracking, 回文串的permuration是通过两个指针进行简单的镜像对称来实现的 在找到的所有的permutation当中判断出来哪些是Palindrome放到res当中。效率太低。

需要判断的是字符串的奇偶性, 用一个oneOdd变量来做标记,如果是奇数个, 最中间一定是一个单字符, 双指针的起点是i-1i+1,如果是偶数个, 起点为ii + 1

public class Solution {
    public List<String> generatePalindromes(String s) {
        int[] hash = new int[256];
        for(int i = 0; i < s.length(); i++){
            int index = (int) s.charAt(i);
            hash[index] ++;
        }
        char center = '.';
        boolean oneOdd = false;
        for(int i = 0; i < 256; i++){
            if(hash[i] % 2 == 0){
                continue;
            } else {
                if(oneOdd) return new ArrayList<String>(); //奇数字符多于一个直接return空字符
                oneOdd = true;
                center = (char) i;
                hash[i] --;
            }
        }

        char[] array = new char[s.length()];
        List<String> list = new ArrayList<String>();
        if(oneOdd) {
            array[s.length() / 2] = center;
            dfs(list, array, hash, s.length() / 2 - 1, s.length() / 2 + 1);
        } else {
            dfs(list, array, hash, s.length() / 2 - 1, s.length() / 2);    
        }

        return list;
    }

    private void dfs(List<String> list, char[] array, int[] hash, int left, int right){
        if(left < 0 || right >= array.length){
            list.add(new String(array));
            return;
        }

        for(int i = 0; i < 256; i++){
            if(hash[i] <= 0) continue;

            array[left] = (char) i;
            array[right] = (char) i;
            hash[i] -= 2;

            dfs(list, array, hash, left - 1, right + 1);

            array[left] = '.';
            array[right] = '.';
            hash[i] += 2;
        }
    }
}
复制代码

相关题目:

46. Permutations [Medium]

  • 思路: Backtracking, 使用一个辅助的used数组来进行判断
class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return res;
        }
        int n = nums.length;
        boolean[] used = new boolean[n];
        helper(res, new ArrayList<>(), used, nums);
        return res;
    }
    
    private void helper(List<List<Integer>> res, List<Integer> curr, boolean[] used, int[] nums) {
        if (curr.size() == nums.length) {
            res.add(new ArrayList(curr));
        }
        for (int i = 0; i < nums.length; i++) {
            if (!used[i]) {
                curr.add(nums[i]);
                used[i] = true;
                helper(res, curr, used, nums);
                used[i] = false;
                curr.remove(curr.size() - 1);
            }
        }
    }
}
复制代码

有重复元素

Arrays.sort(nums) // 排序这样所有重复的数
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1]) { continue; } // 跳过会造成重复的情况
复制代码

78. Subsets [Medium]

  • 思路: for 循环的起点不同
class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> results = new ArrayList<>();
    	Arrays.sort(nums);
    	helper(results, new ArrayList<>(), nums, 0);
    	return results;       
    }
// 1.递归的定义: 在nums中找到所有的subset,每个元素都有两个选择, 加入和不加入
    private void helper(List<List<Integer>> results,
                        List<Integer> subset,
                        int[] nums,
                        int start) {
    //2. 递归的拆解 deep copy -> create new subset ArrayList
    //每一层都加当前的新元素
    //添加顺序: [] [1] [1,2] [1,2,3] [1,3] [2] [2,3] [3]
    
    results.add(new ArrayList(subset));
    for (int i = start; i < nums.length; i++) {
    	//[] -> [1] or [1] -> [1, 2]
    	subset.add(nums[i]);
    	helper(results, subset, nums, i + 1);
    	subset.remove(subset.size() - 1);
    }
    // 3. 递归的出口
    // 当for循环的条件不满足时,直接什么都不执行 => return 
    }
}
复制代码

猜你喜欢

转载自juejin.im/post/5c51823ce51d4556940c282a