【问题标题】:One of the solution for finding the longest palindromic substring could not be understood无法理解找到最长回文子串的解决方案之一
【发布时间】:2015-06-10 09:48:18
【问题描述】:

参考这个article on leetcode,解决最长回文子串问题有一个常见错误:

把 S 倒过来变成 S’。找到 S 和 S' 之间最长的公共子串,它也必须是最长的回文子串。

例如:

S = “abacdfgdcaba”,S' = “abacdgfdcaba”。

S 和 S' 之间最长的公共子串是“abacd”。显然,这不是一个有效的回文。

但是下面的整改我看不太懂。任何人都可以通过分步程序/示例来解释它吗?谢谢!

为了纠正这个问题,每次我们找到一个最长的公共子串候选时,我们检查子串的索引是否与反转子串的原始索引相同。

【问题讨论】:

  • 我认为这不会导致任何有效的算法
  • 我知道有一个 Manacher 算法,它只需要 O(N) 时间复杂度。我只是对这个解决方案感到好奇,它的整改意味着什么? @NiklasB。
  • 他们建议使用 O(n^2) DP 方法来解决 LCS,然后只检查 DP 表中与表示原始字符串中相同子字符串的子字符串相对应的索引。假设我们有 LCS(i, j) = k,那么它对应于回文 if abs(i - (n - 1 - j)) + 1 = k

标签: algorithm palindrome


【解决方案1】:

我被困在那里,所以我用谷歌搜索到这里。现在我明白了。我以作者提到的原始字符串为例。

S = "caba', S' = "abac", so longest common substring is aba.

句子是“我们检查子字符串的索引是否与反转子字符串的原始索引相同。”

1.子串的索引是什么?

"aba" is [1, 2, 3]

2.什么是反转子串的原始索引? 反转子串为“aba”,其原始索引也是[1, 2, 3]。

所以这个答案是正确的。

我们正在研究另一个例子。

S="abacdfgdcaba", S' = "abacdgfdcaba", so longest common substring is "abacd".

所以同样的过程:

1.子串的索引是什么?

"abacd" is [0, 1, 2, 3, 4].

2.什么是反转子串的原始索引? 反转子串是"abacd",但它的原始索引也是[7, 8, 9, 10, 11]。 所以这两个"abacd"不是同一个,答案不正确。

我认为这句话有点棘手,我认为作者让它有点难以理解。 ​

我认为这句话应该改成“为了纠正这个问题,每次我们找到一个最长的公共子串候选时,我们检查子串是否与反转的子串相同”。 ​

【讨论】:

  • 我在这里有点困惑:2.什么是反向子串的原始索引?反转子串是“abacd”,但它的原始索引也是 [7, 8, 9, 10, 11]。所以这两个“abacd”不是同一个,答案是不正确的。你提到了Reversed substring is "abacd", but its original indices is also [7, 8, 9, 10, 11],但它的原始索引应该是[11, 10, 9, 8, 7]
【解决方案2】:

如果有人正在寻找这个问题的实现,我们使用最长公共子串来查找最长回文子串,这是我的版本。

package leetcodeproblems;

/**
 * Created by Nandan Mankad on 23-11-19.
 */
public class LongestPalindromicSubstring {
    public static void main(String[] args) {
        /*String s = "aacdefcaa";*/
        /*String s = "dabcbae";*/
        /*String s = "babad";*/
        /*String s = "cbbd";*/
        /*String s = "cbbc";*/
        String s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        System.out.println(longestPalindrome(s));
    }

    public static String longestPalindrome(String s) {
        if (s == null || s.length() == 0) {
            return s;
        }
        StringBuilder sb = new StringBuilder(s);
        String revString = sb.reverse().toString();  // Reversing the string - to compute LCS.
        int dp[][] = new int[s.length() + 1][s.length() + 1];
        int maxLength = 0;
        int iPos = 0;
        for (int i = 1; i < dp.length; i++) {
            for (int j = 1; j < dp.length; j++) {
                if (s.charAt(i - 1) == revString.charAt(j - 1)) { // Character matches in Original String and Reversed String.
                    dp[i][j] = dp[i - 1][j - 1] + 1;              // Standard Longest Common Substring logic for Original and Reversed String.
                    int revIdx = i;
                    int forIdx = j - dp[i][j] + 1;
                    if (s.length() - revIdx + 1 == forIdx) {      // Here we check if the reverse string original idx is same as original string idx.
                        if (maxLength < dp[i][j]) {
                            maxLength = dp[i][j];
                            iPos = i;
                        }
                    }
                } else {
                    dp[i][j] = 0;
                }
            }
        }

        StringBuilder res = new StringBuilder();
        while (maxLength > 0) {
            res.append(s.charAt(iPos- 1));
            maxLength--;
            iPos--;
        }

        return res.toString();
    }
}

Link

它通过了 Leetcode 上针对该问题的所有测试用例。在我们反向迭代字符串而不是反转实际字符串等情况下,即兴创作可能是可能的。

时间复杂度:O(N^2) 空间复杂度:O(N^2)

【讨论】:

  • 能否请您解释一下您是如何计算 revIndex 和 forIndex 的,它们是什么意思
【解决方案3】:

有一些有效的算法可以通过反转原始子串来计算最长的回文子串。例如:

1) 创建一个广义字符串 S1#S2$,它需要 O(n)
1) 构造后缀数组 O(n) -> 不简单,但有简单的 O(nlogn) 和 O(nlog^2n) 算法
2) 构造一个 lcp 数组 O(n) -> trivial(也有一个平凡的 O(nlogn) )。
3)构造一个RMQ数据结构:O(n)构造和O(1)查询->不平凡(有平凡的O(nlogn)构造和O(logn)查询)
4) 遍历原始字符串 S1 中的每个位置 i。在广义字符串中找到 S2 中的补码位置。找到最长的公共前缀:O(1)

通常,对于偶数和奇数长度的回文数,必须修改上述方法。区别在于,在奇数长度回文中,您只需在选择索引时引入一个间隙。
这产生了该问题的 O(n) 解决方案。

关于文章:
作者提到找到最长的公共子串是不够的,因为具有这样 lcp 的两个子串可能不是原始字符串中的邻居。
因此,我们要找到两个字符串 A 和 B,一个属于 S1,一个属于 S2,这样 lcp(A,B) 最大,而且 A 也是最大的。 rev(B) 在原始 S1 中。

我希望我已经足够清楚了。

【讨论】:

    【解决方案4】:

    我刚开始接触 leetcode,偶然发现了上述文章。

    试图实现完全相同的逻辑。成功了,但效率不及Longest palindromic substring | Dynamic programming 提到的DP解决方案@

    您可以简单地检查范围是否相等以实现算法中的逻辑。

    这是我实现这篇文章的解决方案。运行时间为 364 毫秒,仅比 21.99% 的 C++ 在线提交快

        class Solution {
    public:
        string longestPalindrome(string s) {
            int max = 0,temp,mr=0;
            string revs = s;
            reverse(revs.begin(), revs.end());
            string soln;
            int l = s.size();
            if(l==0 || l==1) return s;
            
            int dp[l+1][l+1];
            
        
            
                for(int row = 0; row < l; row ++)
                for(int col = 0; col < l; col ++){
                    if(s.at(row) != revs.at(col))
                        dp[row+1][col+1] = 0;
                    else{
                            
                            if(row==0 or col ==0) temp = 1;
                            else temp = dp[row][col] + 1;
                            dp[row+1][col+1] = temp;
                            if(temp > max && row-temp+1 == l-col -1 && l-row -1 == col-temp +1 ){
                                    mr = row;max = temp;
                                } 
                            }
                    }
           
            /*        for(int row = 0; row < l+1; row ++){
                for(int col = 0; col < l+1; col ++){
                    if(row == 0 && col > 0)
                        cout << revs.at(col-1) << ", ";
                    else if(col == 0 && row > 0)
                            cout << s.at(row-1) << ", ";
                    else
                    cout << dp[row][col] << ", ";
                }
                cout << endl;
            }
    
           cout << "\n___________\nmax:" << max <<"\nmr: " << mr << "\n"; */
            //return (max>1)?s.substr(mr-max+1,max):s.substr(0,1);
            return s.substr(mr-max+1,max);
        }
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-24
      • 1970-01-01
      • 2020-11-25
      • 2018-12-04
      • 1970-01-01
      • 2019-06-20
      • 1970-01-01
      相关资源
      最近更新 更多