返回

探秘回文串分割的奥秘:破解 LeetCode 131

前端

前言

在算法竞赛和编程实践中,字符串处理是一个不可或缺的领域。LeetCode 131 “分割回文串”便是其中一道经典题目,考察了我们对回文串、动态规划和递归算法的理解。本文将深入解析这道题目,揭示算法背后的精髓,助力读者在算法竞赛和编程实践中更上一层楼。

回文串的奥秘

回文串是指一个从左到右读和从右到左读都相同的字符串。例如,“level”和“racecar”都是回文串。在回文串分割问题中,我们需将给定的字符串分割成若干个子串,且每个子串都是回文串。

暴力回溯:穷举所有可能

解决回文串分割问题的一个朴素方法是暴力回溯。暴力回溯是一种深度优先的搜索算法,它通过穷举所有可能的分割方案,找到最优解。

public List<List<String>> partition(String s) {
    List<List<String>> result = new ArrayList<>();
    backtrack(s, 0, new ArrayList<>(), result);
    return result;
}

private void backtrack(String s, int start, List<String> path, List<List<String>> result) {
    if (start == s.length()) {
        result.add(new ArrayList<>(path));
        return;
    }
    
    for (int i = start; i < s.length(); i++) {
        String substring = s.substring(start, i + 1);
        if (isPalindrome(substring)) {
            path.add(substring);
            backtrack(s, i + 1, path, result);
            path.remove(path.size() - 1);
        }
    }
}

private boolean isPalindrome(String s) {
    int i = 0, j = s.length() - 1;
    while (i < j) {
        if (s.charAt(i) != s.charAt(j)) {
            return false;
        }
        i++;
        j--;
    }
    return true;
}

动态规划:优化效率

暴力回溯虽然能找到最优解,但效率较低,特别是对于较长的字符串。我们可以通过动态规划优化算法的效率。动态规划是一种自底向上的算法,它通过存储子问题的最优解,避免重复计算。

public List<List<String>> partition(String s) {
    int n = s.length();
    boolean[][] dp = new boolean[n][n]; // 记录子串是否为回文串
    List<List<String>>[] memo = new List[n]; // 记录子串的分割方案
    
    for (int i = 0; i < n; i++) {
        dp[i][i] = true;
        memo[i] = new ArrayList<>();
    }
    
    for (int l = 2; l <= n; l++) {
        for (int i = 0; i <= n - l; i++) {
            int j = i + l - 1;
            if (l == 2) {
                dp[i][j] = (s.charAt(i) == s.charAt(j));
            } else {
                dp[i][j] = (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]);
            }
        }
    }
    
    for (int i = 0; i < n; i++) {
        if (dp[i][n - 1]) {
            memo[i].add(s.substring(i, n));
        }
        for (int j = i + 1; j < n; j++) {
            if (dp[i][j - 1] && !memo[j].isEmpty()) {
                for (String right : memo[j]) {
                    memo[i].add(s.substring(i, j) + " " + right);
                }
            }
        }
    }
    
    return memo[0];
}

总结

回文串分割问题看似复杂,但通过暴力回溯和动态规划等算法,我们可以有效地解决它。在算法竞赛和编程实践中,理解算法背后的精髓至关重要,它不仅能帮助我们解决难题,更能让我们在算法的世界中游刃有余。