代码随想录打卡—day57—【回文问题】— 9.2+9.3 回文问题+DP-END

1 647. 回文子串

647. 回文子串 

【解法1】纯暴力解法,应该是O(n^3),居然AC了:

class Solution {
public:
    int countSubstrings(string s) {
        // 暴力
        int cnt = 0;
        cout << s.substr(1,1);
        for(int i = 0; i < s.size();i++)
        {
            for(int j = i; j < s.size();j++)
            {   
                string tmp = s.substr( i , (j-i+1));  // 为O(len)
                string reverse1 = tmp;
                reverse(tmp.begin(), tmp.end());  
                if(reverse1 == tmp)cnt++;
            }
        }
        return cnt;
    }
};

【解法2】动态规划。我按照经验的dp数组含义写,写不出,看了题解才懂。本题的dp数组和以往的不太一样,要回到回文串的本质,判断回文串的方法——当 [i,j] 确定是一个回文串时候,若i-1和j+1对应的字符一样,则可判断是一个回文串。

详见注释,AC代码:

class Solution {
public:
    //int dp[1010]; // dp[i] 以i结尾的连续的字符串是回文子串的最大个数==>组合数目【错误】

    bool dp[1010][1010]; // dp[i][j]表示 s[i,j] 是一个回文串

    /*
        j >= i

        if(s[i] != s[j])
            dp[i][j] = 0;
        else
        {
            if(i == j) // 一个元素
                dp[i][j] = 1;
            else
            {
                if(j == i+1)dp[i][j] = 1;  //两个元素
                else  //至少3个元素
                {
                    if(dp[i+1][j-1])dp[i][j] = 1;
                }
            }
        }

        初始化 dp[i][j] = 0;
        顺序:
            需要左下角是满的 
            i-- j++
        模拟——
    */
   
    int countSubstrings(string s) 
    {
        int ans = 0;

        for(int i = s.size()-1; i >= 0; i--)
        {
            for(int j = i; j < s.size();j++)
            {
                if(s[i] != s[j])
                    dp[i][j] = 0;
                else
                {
                    if(i == j) // 一个元素
                    {
                        dp[i][j] = 1;
                        ans++;
                    }
                    else
                    {
                        if(j == i+1)
                        {
                            dp[i][j] = 1;  //两个元素
                            ans++;
                        }
                        else  //至少3个元素
                        {
                            if(dp[i+1][j-1])
                            {
                                dp[i][j] = 1;
                                ans++;
                            }
                        }
                    }
                }
            }
        }
        return ans;
    }
};

【解法3】双指针法。O(n^2)。

首先确定回文串,就是找中心然后向两边扩散看是不是对称的就可以了。

在遍历中心点的时候,要注意中心点有两种情况

一个元素可以作为中心点,两个元素也可以作为中心点。那么有人同学问了,三个元素还可以做中心点呢。其实三个元素就可以由一个元素左右添加元素得到,四个元素则可以由两个元素左右添加元素得到。所以我们在计算的时候,要注意一个元素为中心点和两个元素为中心点的情况。

AC代码:

class Solution {
public:
    int count(string s,int l,int r)
    {
        int ans = 0;
        while(l >= 0 && r < s.size() && s[l] == s[r])
        {
            l--;
            r++;
            ans++;
        }
        return ans;
    }

    int countSubstrings(string s) 
    {
        int ans = 0;
        for(int i = 0; i < s.size();i++)
        {
            // 以i为单点中心
            ans += count(s,i,i);
            // 以i为双点中心的左中心
            ans += count(s,i,i + 1);
        }
        return ans;
    }
};

5. 最长回文子串 (类似的题)

用中心扩展双指针的做法:

class Solution {
public:
    int ans = 0;
    int max_l,max_r = 0;

    void count(string s,int l,int r)
    {
        while(l >= 0 && r < s.size() && s[l] == s[r])
        {
            l--;
            r++;
        }
        l++;
        r--;

        if(r-l+1 > ans)
        {
            max_l = l;
            max_r = r;
            ans = r-l+1 ;
        }
    }

    string longestPalindrome(string s) {
        for(int i = 0; i < s.size();i++)
        {
            // 以i为单点中心
            count(s,i,i);
            // 以i为双点中心的左中心
            count(s,i,i + 1);
        }
        return s.substr(max_l,max_r - max_l + 1);

    }
};

2 516. 最长回文子序列

516. 最长回文子序列

我在学习完上一题的基础上,直接尝试推导这一题,因为我错误的dp数组含义:以i开头 j结尾的回文子序列的最大长度。结果状态转移方程弄的很复杂。还嵌套两个for循环。应该是能过很多测试点的,在第65个测试点 TLE 了。代码如下:

class Solution {
public:
    int dp[1010][1010]; // 以i开头 j结尾的回文子序列的最大长度
    /*
        if(s[i] != s[j])dp[i][j] = 0
        else
        {
            if(i == j)dp[i][j] = 1;   //一个元素
            else if(j == i+1)dp[i][j] = 2;   //两个元素
            else                             //至少三个元素 
            {
                int tmp = 0;
                for(int k1 = i+1; k1 <= j-1; k1++)
                {
                    for(int k2 = k1; k2<= j-1; k2++)
                    {
                        tmp = max(tmp,dp[k1][k2]);
                    }
                }
                if(tmp == 0)dp[i][j] = 0;
                else dp[i][j] = tmp+2;
            }
        }
        初始化:
            默认为0
        顺序:
            i--j++
        模拟:
        
    */
    int longestPalindromeSubseq(string s) 
    {
        int ans = 0;
        for(int i = s.size()-1; i >= 0; i--)
        {
            for(int j = i; j < s.size();j++)
            {
                if(s[i] != s[j])dp[i][j] = 0;
                else
                {
                    if(i == j)dp[i][j] = 1;   //一个元素
                    else if(j == i+1)dp[i][j] = 2;   //两个元素
                    else                             //至少三个元素 
                    {
                        int tmp = 0;
                        for(int k1 = i+1; k1 <= j-1; k1++)
                            for(int k2 = k1; k2<= j-1; k2++)
                                tmp = max(tmp,dp[k1][k2]);

                        if(tmp == 0)dp[i][j] = 0;
                        else dp[i][j] = tmp+2;
                    }
                }
                ans = max(ans,dp[i][j]);
            }
        }
        return ans;
    }
};

学习完题解,要把dp数组的含义改成:以[i,j]中的回文子序列的最大长度。然后状态转移方程就比较好推导了。

AC代码:

class Solution {
public:
    int dp[1010][1010]; // 以[i,j]中的回文子序列的最大长度
    /*
        if(s[i] == s[j])dp[i][j] = dp[i+1][j-1] + 2;
        else
        {
            dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
                            去掉j元素   去掉i元素
        }
        初始化:
            默认为0,所以正对角线上全设置为1 ,便于状态转移的j循环从i+1开始
        顺序:
            i--j++
        模拟:
        
    */
    int longestPalindromeSubseq(string s) 
    {
        int ans = 1;
        for(int i = 0; i < s.size();i++)dp[i][i] = 1;

        for(int i = s.size()-1; i >= 0; i--)
        {
            for(int j = i+1; j < s.size();j++)
            {
                if(s[i] == s[j])dp[i][j] = dp[i+1][j-1] + 2;
                else
                    dp[i][j] = max(dp[i+1][j],dp[i][j-1]);
                ans = max(ans,dp[i][j]);
            }
        }
        return ans;
    }
};

DP总结

go to

你可能感兴趣的:(SXL,算法)