代码随想录算法训练营day57|647.回文子串516.最长回文子序列 剑指offer39、66

647.回文子串

题目链接

本题主要是确定如何定义合适的dp数组,如果定义一维的数组不好找到递推关系,如果定义二维的,向两边拓展来判断回文子串是比较合适的,所以dp[i][j]表示[i,j]的子串是否是回文子串,注意这里是是否是,而不是直接表示数量。

class Solution {
public:
    int countSubstrings(string s) {
        vector<vector<bool>> dp(s.size(),vector<bool>(s.size(),false));
        int res=0;
        for(int i=s.size()-1;i>=0;i--){
            for(int j=i;j<s.size();j++){
                if(s[i]==s[j]){
                    if(j-i<2){
                        dp[i][j]=true;
                        res++;
                    }else if(dp[i+1][j-1]==true){
                        dp[i][j]=true;
                        res++;
                    }
                }
            }
        }
        return res;
    }
};

516.最长回文子序列

题目链接

比较简单,注意单个字符一定是回文子序列,长度为1。

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        vector<vector<int>> dp(s.size(),vector<int>(s.size(),0));
        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]);
                }
            }
        }
        return dp[0][s.size()-1];
    }
};

错因:往里缩的时候写成了[i-1][j-1],应该是[i+1][j-1]。

剑指 Offer 39. 数组中出现次数超过一半的数字

题目链接

方法一:哈希表。先统计每个元素出现的次数,同时进行判断,如果当前元素出现次数已经超过数组长度一半了,就返回该元素。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        unordered_map<int,int> hash;
        int n=nums.size();
        for(int num:nums){
            hash[num]++;
            if(hash[num]>n/2){
                return num;
            }
        }
        return -1;
    }
};

方法二:排序。因为题目中说了元素出现的次数超过数组长度一半,所以排序后中位数一定是众数。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int index=nums.size()/2;
        return nums[index];
    }
};

空间复杂度是O(1)的,时间复杂度是O(nlogn),也不是最优的。

方法三:摩尔投票法。先假设第一个元素时众数,票数为1,不断向后遍历,同时进行抵消,如果已经抵消为0了,就要重新选取众数,票数为1。因为要求的数字x出现次数超过长度一半了,所以其他所有元素挨个和x抵消一遍,最后剩下的一定还是x。为什么代码中最后留下的票数不为0的一定就是x,因为最后剩下元素,说明该元素的票数没有被抵消完,又因为最后剩下的一定是x,所以最后留下的就是x。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        if(nums.size()<=2) return nums[0];
        int x=nums[0];
        int sum=1;
        for(int i=1;i<nums.size();i++){
            if(sum==0){
                x=nums[i];
                sum=1;
            }else{
                if(x==nums[i]){
                    sum++;
                }else{
                    sum--;
                }
            }
        }
        return x;
    }
};

时间复杂度是O(1),空间复杂度是O(n)。

注意特殊情况的判断,只有一个或两个元素的时候直接返回第一个元素值即可。

剑指 Offer 66. 构建乘积数组

题目链接

正常的思路是先求出所有元素的乘积,然后用for循环遍历,遍历到某元素时用乘积除以该元素即可,但本题不让用除法,所以只能先累乘左边的元素,再累乘右边的元素,再相乘。但是代码还是有一定技巧性的,需要注意。

class Solution {
public:
    vector<int> constructArr(vector<int>& a) {
        if(a.empty()) return vector<int>();
        int n=a.size();
        vector<int> b(n);
        for(int i=0,p=1;i<n;i++){
            b[i]=p;//记录每个元素左边元素的乘积,第一个元素左边元素乘积记为1,p开始也是记录的当前元素左边元素的乘积
            p*=a[i];//这执行了之后p记录的就是下一个元素左边所有元素的乘积
        }
        for(int i=n-1,p=1;i>=0;i--){
            b[i]*=p;//p开始记录的是当前元素右边元素的乘积,又因为b[i]已经记录了当前元素左边元素的乘积了,所以直接相乘即可
            p*=a[i];//开始从右向左累乘
        }
        return b;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值