[LeetCode] Longest Palindromic Substring 最长回文子串

本文介绍两种寻找字符串中最长回文子串的方法:一种通过中心扩展法实现,另一种利用动态规划解决。中心扩展法考虑奇偶两种情况,动态规划法则使用二维数组记录子串的回文状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最长回文子串的问题有很多变种,先看最基本的:

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.

思路: 用一个指针从0遍历到s.length()-1,从里向外来检测子串是否是回文,分两种情况:1)bab, 2)bb。也就是当前指针可能指向的是奇数回文,也可能是偶数回文。做两次检测。

Java

   public static  String longestPalindrome(String s) {  
        if (s == null || s.length() <= 1) return s;  
        String longestPalindrome = "";  
        for (int i = 0; i < s.length() - 1; i++) {  
            String palindrome = getLongestPalindrome(s, i, i);  //case: bab
            if (palindrome.length() > longestPalindrome.length()) {  
                longestPalindrome = palindrome;  
            }  
              
            palindrome = getLongestPalindrome(s, i, i + 1);   //case: bb
            if (palindrome.length() > longestPalindrome.length()) {  
                longestPalindrome = palindrome;  
            }   
        }  
        return longestPalindrome;  
    }  
      
    public static String getLongestPalindrome(String s, int left, int right) {  
        int length = s.length();  
        while(left >= 0 && right < length && s.charAt(left) == s.charAt(right)) {  
            left--;  
            right++;  
        }  
        return s.substring(left + 1, right);  
    }  
	
	public static void main(String[] args){
		
		System.out.println(longestPalindrome("wfacabcdcbagggafd"));
		
	}
}


其他思路:




思路1. 动态规划
这里动态规划的思路是 dp[i][j] 表示的是 从i 到 j 的字串,是否是回文串。
则根据回文的规则我们可以知道:
如果s[i] == s[j] 那么是否是回文决定于 dp[i+1][ j - 1]
当 s[i] != s[j] 的时候, dp[i][j] 直接就是 false。
动态规划的进行是按照字符串的长度从1 到 n推进的。
代码很明晰:给出java代码,复杂度 O(n^2)
[java] view plaincopy

public class DPSolution {  
  
    boolean[][] dp;  
      
    public String longestPalindrome(String s)  
    {  
        if(s.length() == 0)  
        {  
            return "";  
        }  
        if(s.length() == 1)  
        {  
            return s;  
        }  
  
        dp = new boolean[s.length()][s.length()];  
          
        int i,j;  
          
        for( i = 0; i < s.length(); i++)  
        {  
            for( j = 0; j < s.length(); j++)  
            {  
                if(i >= j)  
                {  
                    dp[i][j] = true; //当i == j 的时候,只有一个字符的字符串; 当 i > j 认为是空串,也是回文  
  
                }  
                else  
                {  
                    dp[i][j] = false; //其他情况都初始化成不是回文  
                }  
            }  
        }  
          
        int k;  
        int maxLen = 1;  
        int rf = 0, rt = 0;  
        for( k = 1; k < s.length(); k++)  
        {  
            for( i = 0;  k + i < s.length(); i++)  
            {  
                j = i + k;  
                if(s.charAt(i) != s.charAt(j)) //对字符串 s[i....j] 如果 s[i] != s[j] 那么不是回文  
                {  
                    dp[i][j] = false;  
                }  
                else  //如果s[i] == s[j] 回文性质由 s[i+1][j-1] 决定  
                {  
                    dp[i][j] = dp[i+1][j-1];  
                    if(dp[i][j])  
                    {  
                        if(k + 1 > maxLen)  
                        {  
                            maxLen = k + 1;  
                            rf = i;  
                            rt = j;  
                        }  
                    }  
                }  
            }  
        }  
        return s.substring(rf, rt+1);  
    }  
}  







思路2. KMP匹配
第二个思路来源于字符串匹配,最长回文串有如下性质: 
对于串S, 假设它的 Reverse是 S', 那么S的最长回文串是 S 和 S' 的最长公共字串。
例如 S = abcddca,  S' = acddcba, S和S'的最长公共字串是 cddc 也是S的最长回文字串。
如果S‘是 模式串,我们可以对S’的所有后缀枚举(S0, S1, S2, Sn) 然后用每个后缀和S匹配,寻找最长的匹配前缀。
例如当前枚举是 S0 = acddcba 最长匹配前缀是 a
S1  = cddcba 最长匹配前缀是 cddc
S2 = ddcba 最长匹配前缀是 ddc
当然这个过程可以做适当剪枝,如果当前枚举的后缀长度,小于当前找到的最长匹配,则直接跳过。


Java 代码如下:
[java] view plaincopy
public class Solution {  
    private int[] next;  
    private void GetNext(String s) //KMP求next数组  
    {  
        int i,j;  
          
        i = 0;   
        j = -1;  
          
        next[0] = -1;  
          
        while( i < s.length())  
        {  
            if( j == -1 || s.charAt(i) == s.charAt(j))  
            {  
                i++;  
                j++;  
                next[i] = j;  
            }  
            else  
            {  
                j = next[j];  
            }  
        }  
    }  
    private int compare(String pattern, String s) //用KMP算法做求出最长的前缀匹配  
    {  
        int i,j;  
          
        i = 0;  
        j = 0;  
       
        int maxLen = 0;  
        while( i < s.length())  
        {  
            if(j == -1 || pattern.charAt(j) == s.charAt(i))  
            {  
                i++;  
                j++;  
            }  
            else  
            {  
                j = next[j];  
            }  
            if( j > maxLen)  
            {  
                maxLen = j;  
            }  
            if(j == pattern.length())  
            {  
                return maxLen;  
            }  
        }  
        return maxLen;  
    }  
      
    public String longestPalindrome(String s)  //  
    {  
        // Start typing your Java solution below  
        // DO NOT write main() function  
        String reverString = new StringBuilder(s).reverse().toString();  //求得到 输入string 的reverse  
        next = new int[s.length() + 1];  
          
        String maxPal = "";  
        int maxLen = 0;  
        int len;  
        for(int i = 0; i < s.length(); i++) //枚举所有后缀  
        {  
            String suffix = reverString.substring(i);  
            if(suffix.length() < maxLen)  
            {  
                break;  
            }  
            GetNext(suffix);  
            len = compare(suffix, s);  
            if( len > maxLen)  
            {  
                maxPal = suffix.substring(0, len);  
                maxLen = len;  
            }  
              
        }  
        return maxPal;  
          
    }  
}  







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值