LeetCode 131. Palindrome Partitioning

这篇文章是 LeetCode 131. Palindrome Partitioning 的分析与解法。

问题描述

Given a string s, partition s such that every substring of the partition is a palindrome.

Return all possible palindrome partitioning of s.

For example, given s = "aab", Return

[
  ["aa","b"],
  ["a","a","b"]
]

这道题的意思就是将给定的字符串分成回文串的组合,就像例子中所说,aab有两种回文串组合:aa,ba,a,b.

问题分析

对于这个问题,我们很简单的将它分解为两个子问题:

  • 拆分字符串
  • 判断一个字符串是否是回文串

Step 1 判断回文字符串

如果一个字符串正读和反读结果都一样,我们就说它是一个回文字符串。判断一个字符串是不是回文的有很多种方法,我想起来 3 种方法,都会在接下来的文章中进行介绍,并给出源码(文中的代码皆为 C++)。

反转字符串法

这个方法是最容易理解的,将字符串反转,如果和原来的字符串一样,那么它就是回文的,这个方法在编码上也是最简单的:

bool isPalindrome_reverse(string s, int i, int j){
    string r = s;
    reverse(s.begin(),s.end());
    if(s.compare(r)!=0){
        return false;
    }
    return true;
}

双指针法

双指针法是通过两个指针,一个指向字符串首,另一个指向字符串尾,如果两个指针指向的字符相同,则两个指针向中间移动,继续判断。

bool isPalindrome_doublepoints(string s, int i, int j){
    while(i < j){
        if(s[i] != s[j]){
            return false;
        }
        i++;
        j--;
    }
    return true;
}

递归法

递归法和双指针法很类似,当前字符串是否回文取决于首尾字符是否相同,然后递归的判断除去首尾的剩余字符串是否回文。

bool isPalindrome_recursion(string s, int i,int j){
    if(i == j){
        return true;
    }
    else{
        if(s[i] == s[j]){
            i++;
            j--;
            if(i < j){
                return isPalindrome_recursion(s, i, j);
            }
            else{
                return true;
            }
        }
        else{
            return false;
        }
    }
}

Step 2 拆分字符串

这一步是这个问题的关键,解决拆分字符串的方案也有 2 种:暴力回溯法递归法

暴力回溯法

暴力回溯法比较好理解,它使用的是回溯法的思想,我们穷举出来字符串的所有子串组合,然后判断其中的子串是不是回文的,去掉不符合要求的组合,剩余的就是我们要的结果。

在进行穷举的时候,如果遇到不是回文的子串,我们就进行回溯。

以题目中的aab为例:

实现代码如下:

void backtrace(vector<vector<string>> &vec, vector<string> &temp, string s, int start){
    if(start == s.length()){
        vec.push_back(temp);
    }
    else{
        for(int i = start; i < s.length(); i++){
            if(isPalindrome(s, start, i)){
                temp.push_back(s.substr(start, i-start+1));
                backtrace(vec, temp, s, i+1);
                temp.pop_back();
            }
        }
    }
}

递归法

递归法的思路是把一个字符串分为 A+B,如果 A 为回文则递归的求 B 的回文组合,然后将 A 和 B 的回文串组合做笛卡尔积。

以字符串 aabb 为例:

  1. 将aabb 分为 a+abb,然后求 abb 的回文组合为[a, b, b], [a, bb],所以做笛卡尔积后为:[a, a, b,b ], [a, a, bb]
  2. 将字符串分为 aa+bb,然后求 bb 的回文组合为[b, b], [bb],结果为[aa, b, b], [aa, bb]
  3. 将字符串分为 aab+b,aab 不回文
  4. aabb 回文,结果为[aabb]
  5. 最终结果为:[a, a, b,b ], [a, a, bb], [aa, b, b], [aa, bb], [aabb]

实现代码如下:

vector<vector<string>> partition_recursion(string s){
    vector<vector<string>> vec;

    if(s.length() == 0){
        return vec;
    }

    if(isPalindrome_recursion(s, 0, s.length()-1)){
        vector<string> temp;
        temp.push_back(s);
        vec.push_back(temp);
    }

    for(int i = 1; i <= s.length(); i++){
        string left = s.substr(0, i);
        if(isPalindrome(left, 0, left.length()-1)){
            string right = s.substr(i, s.length()-i);
            vector<vector<string>> rightVec = partition_recursion(right);
            if(rightVec.size() > 0){
                for(int j = 0; j < rightVec.size(); j++){
                    vector<string> temp;
                    temp.push_back(left);
                    for(int x = 0; x < rightVec[j].size(); x++){
                        temp.push_back(rightVec[j][x]);
                    }
                    vec.push_back(temp);
                }
            }
        }
    }  
    return vec;
}

结果测试

将几种方法组合后的测试结果如下:

反转字符串法双指针法递归法
暴力回溯法16 ms13 ms
递归法89 ms76 ms

我们看到回溯法要明显优于递归的方法。

本文的完整代码详见我的 GitHub


本文的版权归作者 罗远航 所有,采用 Attribution-NonCommercial 3.0 License。任何人可以进行转载、分享,但不可在未经允许的情况下用于商业用途;转载请注明出处。感谢配合!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值