经典面试题-最长回文子串(双指针、dp)/子序列(dp)

子串问题

子串问题一般是枚举每个位置作为(开始/结束/中间),然后再整个串中找最大/最小,例如:
回文子串(枚举每一个字符作为中间字符的情况)
无重复子串(枚举每一个字符作为子串的开始字符的情况)
子串问题先考虑双指针(例如滑动窗口),子序列考虑动态规划

经典面试题-最长回文子串

双指针

回文串的的长度可能是奇数,也可能是偶数,这就添加了回文串问题的难度,解决该类问题的核心是双指针

关于使用双指针的问题总结
labuladong

这道题如何使用双指针呢?
寻找回文串的问题核心思想是:从中间开始向两边扩散来判断回文串

遍历字符串中的字符,找以每个字符为中心的最大回文子串,然后再找整个字符串的最大回文子串

for 0 <= i < len(s):
    找到以 s[i] 为中心的回文串
    找到以 s[i] 和 s[i+1] 为中心的回文串
    更新答案

怎么找以s[i](或s[i]和s[i+1])为中心的回文串呢?

string plalindrome(int l,int r,string &str1)
{
    string str=str1;
    int len=str.size();
    while(l>=0&&r<len&&s[l]==s[r])//因为子串必须是连续的
    {
        l--;r++;//向两边展开
    }
    return str.substr(l+1,r-l-1);//因为是l--和r++过的
}

所以整体的代码如下:

string fun(string &ss)
{
    string s=ss;
    string res;
    for(int i=0;i<s.size();i++)
    {
        s1=plalindrome(i,i,s);
        s2=plalindrome(i,i+1,s);
        res=s1.size()>s2.size()?s1:s2;
    }
    return res;
}



动态规划

动态规划也可以解决,时间复杂度一样,但是空间复杂度为 O ( n 2 ) O(n^2) O(n2) 存储dp[][]
二维数组dp[i][j]=1表示s[i…j]是最长回文子串,dp[i][j]=0表示不是,那么如果dp[i][j]=1那么dp[i+1][j-1]也应该等于1,否则dp[i][j]将不是回文串所以

状态转移方程为:
在这里插入图片描述
base case:
dp[i][i]=1
dp[i][i+1]=1 if str[i]==str[i+1]
还有一个字符和两个相同字符的子串都是回文串

用双指针解决的时候,遍历字符串是枚举回文串的中心字符,这里是遍历字符串枚举回文串的起始字符。

string longestplatindrome(stirng s)
{
    int start,len;//回文串的开始和长度
    
    //base case
    for(int i=0;i<s.size();i++)
    {
        dp[i][i]=1;start=i;len=1;
        if(i<len-1&&s[i]==s[i+1])dp[i][i+1])dp[i][i+1]=1;
        {
            start=i;
            len=2;
        }
    }
    
    //枚举回文串的开始字符
    for(int l=3;l<=s.size();l++)
        for(int i=0;i+l-1<s.size();i++)
    {
        if(s[i]==s[i+l-1]&&dp[i+1][i+l-2]=1)
        {
            len=l;
            start=i;
        }
    }
    
    return s.substr(i,len);
}

最长回文子序列

首先明确,

子序列和子串的区别:

子串必须是连续的,子序列不要求是连续的

所以最长回文字序列中,如果用动态规划,if(dp[i+1][j-1]=0)即不是回文子串,那么dp[i][j]一定也不是回文子串,通过这个,就可以从base case通过枚举所有子串起始位置来得到最长子串;当然,子串用双指针更简单,从中间向两侧延伸,遍历子串的中间字符(一个或者两个)

子序列可以是不连续的,思路就像最长上升子序列靠拢,但因为他是回文,设计两侧对称,因此考虑用一个二维数组去存放dp table

dp[i][j]表示是s[i…j]的最长回文子序列
base case:一个字符的肯定是回文子序列,同时,如果i>j那么不存在回文子序列。所以:
dp[i][i]=1
dp[i][j]=0 (i>j)
状态转移方程:对于dp[i][j]怎么求呢?
已知dp[i][i]那么,dp[i-1][i+1]?如果s[i-1]=s[i+1],那么s[i-1][i+1]就是长度为3的回文子序列,按照这个思路类推,dp[i][j]可以从dp[i+1][j-1]推理,
s[i]=s[j],dp[i][j]=dp[i+1][j-1]+2,
s[i]!=s[j],s[i]和s[j]分别加入s[i+1…j-1],dp[i][j]=max(dp[i][j-1],dp[i+1][j])

最长回文子序列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 回文子串是指正着读和倒着读都一样的字符串。要求找出一个字符串中最长回文子串。 解决这个问题的方法有很多,其中一种比较简单的方法是暴力枚举。具体来说,我们可以枚举所有可能的子串,然后判断每个子串是否是回文串,最后找出最长回文子串。 具体实现时,我们可以从字符串的第一个字符开始,依次枚举所有可能的子串。对于每个子串,我们可以使用双指针法来判断它是否是回文串。具体来说,我们可以使用两个指针分别指向子串的首尾字符,然后依次向中间移动,判断对应的字符是否相等。如果所有字符都相等,那么这个子串就是回文串。 在判断完所有子串之后,我们就可以找出最长回文子串了。具体来说,我们可以用一个变量来记录当前找到的最长回文子串的长度,以及它的起始位置。在枚举所有子串的过程中,如果找到了一个更长的回文子串,那么就更新这个变量。 需要注意的是,由于字符串中可能存在偶数长度的回文子串,因此我们需要分别枚举以每个字符为中心的奇数长度回文子串和以每两个相邻字符为中心的偶数长度回文子串。 这种暴力枚举的方法时间复杂度为O(n^3),其中n是字符串的长度。虽然时间复杂度比较高,但是这种方法比较简单易懂,可以作为其他更高效的算法的基础。 ### 回答2: 回文子串是指正着读和倒着读一样的字符串。例如,'level'和'noon'就是回文子串。编写程序来寻找给定字符串中最长回文子串。 一种常见的方法是暴力枚举字符串中的所有子串并检查它们是否是回文。这种方法的时间复杂度为O(n^3),不适用于长字符串。另一种常见的方法是动态规划。这种方法的时间复杂度为O(n^2),适用于较长的字符串。 动态规划方法的主要思路如下: 1.定义状态:dp[i][j]表示从i到j的子串是否为回文。 2.初始化状态:对于所有i,dp[i][i]都是true。 3.状态转移:当s[i] = s[j]时,如果dp[i+1][j-1]是true,那么dp[i][j]也是true。 4.记录最长回文子串的起始位置和长度:遍历dp数组,找到值为true且长度最大的子串即可。 下面是一个Python实现的例子: ```python def longest_palindrome(s:str) -> str: if not s: # 处理输入为空的情况 return "" n = len(s) dp = [[False] * n for _ in range(n)] # 初始化dp数组 start, max_len = 0, 1 # 记录最长回文子串的起始位置和长度 for i in range(n): # 初始化对角线上的状态 dp[i][i] = True for j in range(1, n): for i in range(0, j): if s[i] == s[j]: if j - i < 3: # 特判长度小于3的情况 dp[i][j] = True else: dp[i][j] = dp[i+1][j-1] else: dp[i][j] = False if dp[i][j]: # 记录最长回文子串的起始位置和长度 if j - i + 1 > max_len: max_len = j - i + 1 start = i return s[start:start+max_len] ``` 该算法的时间复杂度为O(n^2),空间复杂度也为O(n^2)。 ### 回答3: 回文串指正着读和倒着读都一样的字符串,例如 "level", "noon" 等。在一个字符串中,可能不存在回文子串,也可能存在多个长度相同的回文子串。现在给定一个字符串,需要我们求出其中最长回文子串。 解决此问题,可以用动态规划算法。假设字符串的长度为 n,定义一个二维数组 dp (n * n),其中 dp[i][j] 表示从 i 到 j 这一段字符串是否为回文串。当字符串为空或长度为 1 时,皆为回文串(即 dp[i][i] = true)。当字符串长度大于等于 2 时,若第 i 个字符和第 j 个字符相等,且从 i + 1 到 j - 1 的字符串也是回文串,那么从 i 到 j 的字符串也是回文串(即 dp[i][j] = dp[i + 1][j - 1] && s[i] == s[j])。此外,记录回文子串的起始位置和长度,最终找到最长回文子串即可。 以下是 Python 代码实现: ``` class Solution: def longestPalindrome(self, s: str) -> str: n = len(s) dp = [[False] * n for _ in range(n)] res = "" for i in range(n - 1, -1, -1): for j in range(i, n): dp[i][j] = s[i] == s[j] and (j - i < 3 or dp[i + 1][j - 1]) if dp[i][j] and j - i + 1 > len(res): res = s[i:j+1] return res ``` 时间复杂度为 O(n^2),空间复杂度为 O(n^2)。总的来说,动态规划算法是解决最长回文子串问题的一种行之有效的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值