【今日算法】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的三次方,显然是一个很差劲的算法,但是这个思路对于我们来说有很大的启发意义

解法二:动态规划

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

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

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

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

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

我们来理一下思路:

  • 首先我们需要创建一个名为dp的二维数组,并把数组的元素初始化为0,将来这个二维数组里面的值表示i-j位置回文串的长度
  • 其次,我们需要从下往上填写dp数组,如果i+1>=j,那么dpij=j-i+1;如果不满足的话,该位置的值一定可以用dpi+1j-1+2去表示,但是前提条件是dpi+1j-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;
    }
};
相关推荐
8Qi82 小时前
回文子串(Palindromic Substrings)—— 题解
算法·leetcode·职场和发展·动态规划
小宋加油啊7 小时前
机械臂抓取物体 PVN3D算法调研学习
学习·算法·3d
lqqjuly7 小时前
前沿算法深度解析(一)
算法
小欣加油7 小时前
leetcode1926 迷宫中离入口最近的出口
数据结构·c++·算法·leetcode·职场和发展
happymaker06269 小时前
LeetCodeHot100——42.接雨水
算法
阿正的梦工坊10 小时前
【Rust】07-错误处理:Option、Result 与 ? 运算符
开发语言·算法·rust
八解毒剂11 小时前
数据结构-平衡二叉树——对二叉搜索树的优化
数据结构·c++·算法
运行时记录12 小时前
别再手动写提示词了 — SkillOpt 让技能文档自己进化
算法
啦啦啦啦啦zzzz12 小时前
算法总结(二分查找、双指针)
c++·算法