【今日算法】LeetCode 5.最长回文子串 和 287.寻找重复数

文章目录

  • 最长回文子串
    • 题目描述
    • 思路分析
      • 解法一:暴力枚举(会超时)
      • 解法二:动态规划
    • 代码书写
      • 解法一:暴力枚举
      • 动态规划
  • 寻找重复数
    • 题目描述
    • 思路分析
    • 代码书写

最长回文子串

题目描述

给你一个字符串 s,找到 s 中最长的 回文 子串。

  • 示例1:

输入:s = "babad"

输出:"bab"

解释:"aba" 同样是符合题意的答案。

  • 示例2:

输入:s = "cbbd"

输出:"bb"

思路分析

本题是让我们寻找最长的回文子串,那么我们想一想什么是回文串:

  • 所谓回文串就是双指针一个从前遍历,一个从后遍历,如果每一个位置的值都相等,那么这个字符串就叫做回文串
  • 单个字符也叫回文串
  • 两个连续的相等字符也叫回文串

解法一:暴力枚举(会超时)

那么根据上面对回文串的分析,我们首先想到双指针遍历,判断该串是否为回文串,left指针从前面开始,right指针从后面开始,比较每一个位置字符是否相等,如果不相等那么该字符串就不是回文串,如果两个指针相遇了,那么就判断该字符串是一个回文子串;或者left+1>=right并且left位置的值等于right位置的值,那么这个字符串也是一个回文串,所以我们需要写一个判断字符串是否是回文串的函数

然后我们就需要定义双指针去遍历整个字符串,但是这里虽然看似是双指针对撞,但是双指针对撞并不能解决问题,因为会遗漏很多情况,所以我们必须使用循环遍历,这样的话时间复杂度就到了O(N^2),与此同时我们判断字符串是否是回文是一个O(N)的时间复杂度,这样算下来,整个算法的时间复杂度就到达了N的三次方,显然是一个很差劲的算法,但是这个思路对于我们来说有很大的启发意义

解法二:动态规划

那么对于动态规划的问题我们一般遵循四个步骤:

  • 状态表示
  • 状态转移方程
  • 初始化
  • 填表顺序

根据上面解决动态规划问题的四个步骤,我们来思考一下为什么能够用动态规划去解决问题:

  • 首先我们来看回文串的定义:从前往后与从后往前字符串相等,那么如果我们定义一个字符数组,那么这个关系就可以表示为dp[i]==dp[j],i<=j,此时我们发现这似乎就是一个状态转移方程;
  • 与此同时我们发现我们可以用一个二维数组去表示i-j位置是否能形成回文串,如果能形成回文串,那么我们就将这个回文串的长度填写到这个二维数组中去,此时我们发现这似乎就是一个状态表示
  • 最后我们来看这么一个问题,如果我们要判断i-j位置的字符串是否是回文串,那么i+1-j-1位置必定是一个回文串,换句话说就是,如果s[i]==s[j],并且dp[i+1][j-1]!=0,那么我们就能够知道i-j必定是一个回文串,此时这个回文串的长度只需要让dp[i+1][j-1]+2即可,如果我们能够画出这个二维数组,那么我们能够看到dp[i+1][j-1]一定在dp[i][j]的左下角,所以我们这个dp表的填写顺序一定是从下往上

根据上面的分析,我们不难发现,这道题一定可以用动态规划来解答,但这里还有一个小小的优化,我们发现i位置一定是在j位置的前面,也就是说,我们只需要关注dp表一半的元素即可

我们来理一下思路:

  • 首先我们需要创建一个名为dp的二维数组,并把数组的元素初始化为0,将来这个二维数组里面的值表示i-j位置回文串的长度
  • 其次,我们需要从下往上填写dp数组,如果i+1>=j,那么dp[i][j]=j-i+1;如果不满足的话,该位置的值一定可以用dp[i+1][j-1]+2去表示,但是前提条件是dp[i+1][j-1]!=0
  • 最后我们需要更新最长数组的值,并记录起始位置,方便我们后面截取子串并返回

代码书写

解法一:暴力枚举

cpp 复制代码
class Solution {
public:
    bool isPalindrome(string s,int left,int right)
    {
        while(left<=right)
        {
            if(s[left]!=s[right]){return false;}
            ++left;
            --right;
        }
        return true;
    }
    string longestPalindrome(string s) {
        int n=s.size();
        int maxLen=0;
        int start=0;
        for(int i=0;i<n;++i)
        {
            for(int j=i;j<n;++j)
            {
                if(j-i+1<=maxLen){ continue;}
                if(isPalindrome(s,i,j))
                {
                    maxLen=j-i+1;
                    start=i;
                }
            }
        }
        return s.substr(start,maxLen);
    }
};

动态规划

cpp 复制代码
class Solution {
public:
    string longestPalindrome(string s) {
        int n=s.size();
        vector<vector<int>> dp(n,vector<int>(n,0));
        int maxLen=0;
        int start=0;
        for(int i=n-1;i>=0;--i)
        {
            for(int j=i;j<n;++j)
            {
                if(s[i]==s[j])
                {
                    if(i+1>=j){dp[i][j]=j-i+1;}
                    else if(dp[i+1][j-1]){dp[i][j]=dp[i+1][j-1]+2;}
                }
                if(maxLen<dp[i][j]){
                    maxLen=dp[i][j];
                    start=i;
                }
            }
        }
        return s.substr(start,maxLen);
    }
};

寻找重复数

题目描述

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

  • 示例1:

输入:nums = [1,3,4,2,2]

输出:2

  • 示例2:

输入:nums = [3,1,3,4,2]

输出:3

  • 示例3:

输入:nums = [3,3,3,3,3]

输出:3

思路分析

本题我们需要抓住一个关键的点,就是数组的元素个数是n+1,数组中元素的范围都是[1,n],也就是说数组下标的范围是[0,n],我们可以惊奇的发现:数组中元素的范围与数组下标的范围是相同的

那么为了维持这样的数组的存在,数组中必定至少会存在两个相同的元素

根据上面的分析,我们这里可以定义两个变量,这两个变量的值是将数组的元素作为下标,得到的,我们只需要让这两个变量去遍历整个数组,然后判断这两个位置的值是否相等,如果相等,那么此时的下标就是重复的元素。

那么此时的问题就是我们该如何去遍历?如果两个变量都从最开始去遍历,那么必然会导致它们两个变量所对应的值处处相等,所以我们必须让两个变量从不同的位置开始遍历

那么我们该如何让两个变量从不同的位置开始遍历呢?我们可以让一个变量向后移动一次,另一个变量向后移动两次,由于有相同元素的存在,它们两个变量的值一定会相等,等它们两个相等后,让其中一个变量从头开始,另一个变量从当前位置开始遍历,这样等他们再次相遇的时候,所对应的数组下标就是相同的元素

代码书写

cpp 复制代码
class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int fast=nums[nums[0]];
        int slow=nums[0];
        while(fast!=slow)
        {
            fast=nums[nums[fast]];
            slow=nums[slow];
        }
        slow=0;
        while(fast!=slow)
        {
            fast=nums[fast];
            slow=nums[slow];
        }
        return slow;
    }
};
相关推荐
你撅嘴真丑2 小时前
第九章-数字三角形
算法
uesowys3 小时前
Apache Spark算法开发指导-One-vs-Rest classifier
人工智能·算法·spark
ValhallaCoder3 小时前
hot100-二叉树I
数据结构·python·算法·二叉树
董董灿是个攻城狮3 小时前
AI 视觉连载1:像素
算法
智驱力人工智能3 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
孞㐑¥4 小时前
算法——BFS
开发语言·c++·经验分享·笔记·算法
月挽清风4 小时前
代码随想录第十五天
数据结构·算法·leetcode
XX風4 小时前
8.1 PFH&&FPFH
图像处理·算法
NEXT065 小时前
前端算法:从 O(n²) 到 O(n),列表转树的极致优化
前端·数据结构·算法
代码游侠5 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法