longest-palindromic-substring

本文介绍了一种寻找字符串中最长回文子串的方法,并通过动态规划的方式实现了该算法。通过对字符串进行遍历并更新状态矩阵,最终找到满足条件的最长回文子串。

Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring.

def longestPalindrome(S):
   if len(S) == 0 or len(S) < 2:
      return S
   dp = [[False for _ in range(len(S))] for _ in range(len(S))]
   res = ''
   maxlen = 0
   dp[0][0] = True
   j = 0
   while j < len(S):
      dp[j][j] = True
      i = 0
      while i < j:
         dp[i][j] = (j - i < 2 or dp[i + 1][j - 1]) and S[i] == S[j]
         if dp[i][j] and j-i+1 > maxlen:
            res = S[i:j+1]
            maxlen = j-i+1
         i += 1
      j += 1
   return res
print longestPalindrome("etretretdaadaad")


#include <iostream> #include <string> #include <vector> using namespace std; class Solution { public: string longestPalindrome(string s) { int n = s.size(); if (n < 2) { return s; } int maxLen = 1; int begin = 0; // dp[i][j] 表示 s[i..j] 是否是回文串 vector<vector<int>> dp(n, vector<int>(n)); // 初始化:所有长度为 1 的子串都是回文串 for (int i = 0; i < n; i++) { dp[i][i] = true; } // 递推开始 // 先枚举子串长度 for (int L = 2; L <= n; L++) { // 枚举左边界,左边界的上限设置可以宽松一些 for (int i = 0; i < n; i++) { // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得 int j = L + i - 1; // 如果右边界越界,就可以退出当前循环 if (j >= n) { break; } if (s[i] != s[j]) { dp[i][j] = false; } else { if (j - i < 3) { dp[i][j] = true; } else { dp[i][j] = dp[i + 1][j - 1]; } } // 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置 if (dp[i][j] && j - i + 1 > maxLen) { maxLen = j - i + 1; begin = i; } } } return s.substr(begin, maxLen); } }; 作者:力扣官方题解 链接:https://leetcode.cn/problems/longest-palindromic-substring/solutions/255195/zui-chang-hui-wen-zi-chuan-by-leetcode-solution/ 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。帮我解释一下for (int L = 2; L <= n; L++) { // 枚举左边界,左边界的上限设置可以宽松一些 for (int i = 0; i < n; i++) { // 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得 int j = L + i - 1; // 如果右边界越界,就可以退出当前循环 if (j >= n) { break; 这里如何确定右边界
07-07
在代码中,右边界 `j` 是通过子串长度 `L` 和左边界 `i` 计算得出的,具体逻辑如下: ### 1. **右边界 `j` 的计算公式** ```cpp int j = L + i - 1; ``` - **`L`** 是当前枚举的子串长度(从 `2` 开始,因为长度为 `1` 的子串已在初始化时处理)。 - **`i`** 是子串的左边界索引。 - **`j`** 是子串的右边界索引,由 `L` 和 `i` 推导得出。 ### 2. **公式的推导过程** 子串的长度 `L` 定义为: \[ L = j - i + 1 \] 通过移项可得: \[ j = L + i - 1 \] - 例如,当 `L=2`(子串长度为 2)、`i=0`(左边界为 0)时: \[ j = 2 + 0 - 1 = 1 \] 此时子串为 `s[0..1]`,长度为 `1 - 0 + 1 = 2`,符合预期。 ### 3. **越界检查** ```cpp if (j >= n) { break; } ``` - 如果 `j` 超出字符串长度 `n`(即 `j >= n`),说明当前左边界 `i` 无法形成长度为 `L` 的子串,直接跳出内层循环。 - 例如,当 `n=5`、`L=5`、`i=1` 时: \[ j = 5 + 1 - 1 = 5 \] 但字符串索引最大为 `4`(`0` 到 `4`),因此 `j=5` 越界,触发 `break`。 ### 4. **枚举逻辑总结** 1. **外层循环**:枚举子串长度 `L`(从 `2` 到 `n`)。 2. **内层循环**:枚举左边界 `i`(从 `0` 开始)。 - 计算右边界 `j = L + i - 1`。 - 如果 `j` 越界,则终止内层循环(因为更大的 `i` 只会让 `j` 更远)。 3. **动态规划填充**: - 如果 `s[i] == s[j]`,则根据子串长度进一步判断: - 若长度 `≤ 3`(即 `j - i < 3`),直接标记为回文(因为去头去尾后无需检查)。 - 否则,依赖 `dp[i+1][j-1]` 的结果(即内部子串是否为回文)。 ### 5. **示例演示** 以字符串 `s = "babad"`(`n=5`)为例: - **`L=2`**: - `i=0` → `j=1` → 检查 `s[0..1]="ba"`(非回文)。 - `i=1` → `j=2` → 检查 `s[1..2]="ab"`(非回文)。 - ...(依此类推)。 - **`L=3`**: - `i=0` → `j=2` → 检查 `s[0..2]="bab"`(是回文,更新 `maxLen` 和 `begin`)。 ### 关键点 - **右边界 `j` 的确定**:通过 `L` 和 `i` 计算,确保子串长度为 `L`。 - **越界处理**:及时终止无效枚举,避免无效计算。 - **动态规划优化**:利用 `dp` 表避免重复计算,提升效率。 ### 相关问题 1. 为什么 `j = L + i - 1` 能正确计算右边界? 2. 在动态规划中,为什么需要先处理短子串再处理长子串? 3. 如果字符串长度 `n` 很大,如何优化这段代码的空间复杂度? 4. 为什么当 `j - i < 3` 时可以直接判定 `dp[i][j] = true`? 5. 如何修改代码以统计所有最长回文子串(而不仅是一个)?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值