Leetcode(W7):5. Longest Palindromic Substring(动态规划)

前言:每次看到别人奇妙的解题思路都要佩服一番大神们的能力还有算法本身的魅力。
本文参考自这位大牛!


1. Longest Palindromic Substring

介绍:

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

题意:

找最长的回文子字符串。

举例:

Input: “babad”
Output: “bab”
Note: “aba” is also a valid answer.
注意,两个相等的字符是回文,单个字符本身也算回文。

思路:

建立一个数据类型为bool的二维数组table,初始化为全false。
其中table[i][j]为true表示字符串s的下标i到j的部分为回文,false则表示不是回文。
动态规划类似于分治算法,要求原问题的解,先求出更小一些的子问题的解,由子问题的解通过某种方式得到原问题的解。
在这里我们要找字符串s的最长回文子字符串(LPS),那么可以先求出s减去首或尾一个字符的部分的LPS,进而可以再往前推,再减去首或尾的一个字符,然后求LPS……
因此,推到最后,我们会发现可以把原问题拆成s.size()个单字符,先遍历整个s求单个字符是不是回文(明显是),然后再遍历整个s求两个字符一组的子字符串是不是回文,然后再遍历整个s去判断三个字符一组的版本……直到最后遍历整个s去判断s.size()个字符一组的版本

操作:

1、求单个字符是不是回文,table[i][i]就是表示下标i的字符本身,这个值必须初始化为true
2、求两个字符一组的子字符串是不是回文,判断s[i]和s[i+1],如果相等则table[i][i+1]为true
3、求N个字符一组的子字符串是不是回文,遍历的时候,因为已经事先知道了N个一组,所以每次都抽N个出来,比如首部下标为a,尾部下标为b。
①判断首尾是否相等;
②然后设aa为首部下标a+1,bb为尾部下标b-1,aa到bb即去除首尾的中间部分,判断中间部分是否回文即判断table[aa][bb]是否为true。
①②都为true才可以确定这个有N个字符的组是回文,那么就可以标记table[a][b]为true。
然后s继续往后遍历,再抽N个字符出来作为一组……直到遍历到s的尾端。
进入下一轮循环,从s首部重新遍历,每次抽N+1个字符。
这样N从最小到最大来做的好处就是,N小的时候算的那些table值可以为N大的时候的计算服务,比如上面提到的table[aa][bb],这个值很明显是在算table[a][b]的前两轮求到的(很明显当N为3的时候要用到N为1的时候的table,N为4的时候要用到N为2的时候的table,所以这也是为什么N为1和2要单独拿出来讨论的原因= =)。
而这正是动态规划效率的高效之处:避免了同一子问题的重复计算
每一轮找到回文都要更新a的值保存在begin中,更新N的值保存在maxlen中,最后只需要把从下标begin,长度为maxlen的子字符串返回即可。

2. 代码

class Solution {
public:
    string longestPalindrome(string s) {
        int size = s.size();
        int begin = 0, maxlen = 1;
        bool table[1000][1000] = {false};  
        //单个字符的判断,明显得全是true 
        for (int i = 0; i < size; i++) {
            table[i][i] = true;
        }
        //两个字符一组
        for (int i = 0; i < size-1; i++) {
            if (s[i] == s[i+1]) {
                table[i][i+1] = true;
                begin = i;
                maxlen = 2;
            }
        }
        //三个字符及以上的一组遍历 
        for (int N = 3; N <= size; N++)
        {
            //拆len个字符出来作为一组 
            for (int a = 0; a < size-N+1; a++)//拆出来的子串首部a 
            {
                int b = a+N-1;//子串尾部b 
                if (s[a] == s[b] && table[a+1][b-1])
                {
                    table[a][b] = true;
                    begin = a;
                    maxlen = N; 
                }
            }
        }
        return s.substr(begin, maxlen);
    }
};

3. 后记

有时候知道思路,但是不知道怎么写代码;
有时候知道代码,但是不知道怎么描述思路;
都很难受!

你可能感兴趣的:(Leetcode)