数据结构算法题/最长回文子串

回文表示字符串正向和反向是相同的。例如a, aba, abccba

一、暴力法

最容易想到的就是暴力破解,求出每一个子串,之后判断是不是回文,找到最长的那个。

求每一个子串时间复杂度O(N^2), 判断子串是不是回文O(N),两者是相乘关系,所以时间复杂度为O(N^3)。

二、动态规划

下面介绍动态规划的方法,使用动态规划可以达到最优的 O(n2) 复杂度。

  令 dp[i][j] 表示 S[i] 至 S[j] 所表示的子串是否是回文子串,是则为 1,不是则为 0。这样根据 S[i] 是否等于 S[j] ,可以把转移情况分为两类:

  1.  若 S[i] == S[j],那么只要 S[i+1] 至 S[j-1] 是回文子串,S[i] 至 S[j] 就是回文子串;如果S[i+1] 至 S[j-1] 不是回文子串,则 S[i] 至 S[j] 也不是回文子串。
  2.  若 S[i] != S[j],那么 S[i] 至 S[j] 一定不是回文子串。    

  根据递推写法从边界出发的原理,注意到边界表示的是长度为 1 和 2 的子串j=i表示长度为1的,i-j=1表示长度为2的,且每次转移时都对子串的长度减了 1,因此不妨考虑按子串的长度和子串的初始位置进行枚举,即第一遍将长度为 3 的子串的 dp 值全部求出,第二遍通过第一遍结果计算出长度为 4 的子串的 dp 值 

public class LongestPalindromicSubstring {
    public String longestPalindrome(String s)
    {
        int n = s.length();
        boolean dp[][] = new boolean[n][n];

        int maxlen = 1;     //保存最长回文子串长度
        int start = 0;      //保存最长回文子串起点
        for(int i = 0; i < n; ++i)
        {
            for(int j = 0; j <= i; ++j)
            {
                if(i - j < 2)//包含了i=j和i-j=1的情况
                {
                    dp[j][i] = (s.charAt(i) == s.charAt(j));
                }
                else
                {
                    dp[j][i] = (s.charAt(i) == s.charAt(j) && dp[j + 1][i - 1]);
                }

                if(dp[j][i] && maxlen <= i - j + 1)//j到i之间的子串是回文子串
                {
                    maxlen = i - j + 1;
                    start = j;
                }
            }
        }
        return s.substring(start, start + maxlen);
    }

    public static void main(String[] args) {
        LongestPalindromicSubstring longestPalindromicSubstring = new LongestPalindromicSubstring();
        System.out.println(longestPalindromicSubstring.longestPalindrome("QATZJUJZTA"));
    }
}

三、中心扩展法

中心扩展就是把给定的字符串的每一个字母当做中心,向两边扩展,这样来找最长的子回文串。算法复杂度为O(N^2)。
需要考虑两种情况:
长度为奇数的回文串,比如a, aba, abcba
长度为偶数的回文串,比如aa, abba

public String longestPalindrome2(String s)
{
    int len = s.length();
    int maxlen = 1;
    int start = 0;

    for(int i = 0; i < len; i++)//求长度为奇数的回文串
    {
        int j = i - 1, k = i + 1;//j和k分别是i的两边
        while(j >= 0 && k < len && s.charAt(j) == s.charAt(k))
        {
            if(k - j + 1 > maxlen)
            {
                maxlen = k - j + 1;
                start = j;
            }
            j--;
            k++;
        }
    }

    for(int i = 0; i < len; i++)//求长度为偶数的回文串
    {
        int j = i, k = i + 1;//奇数和偶数的区别在于j的初始值
        while(j >= 0 && k < len && s.charAt(j) == s.charAt(k))
        {
            if(k - j + 1 > maxlen)
            {
                maxlen = k - j + 1;
                start = j;
            }
            j--;
            k++;
        }
    }
    return s.substring(start, start + maxlen);
}

四、Manacher算法

Manacher算法的时间复杂度为O(N),具体可参考:

https://blog.csdn.net/qq_32354501/article/details/80084325
https://www.cnblogs.com/grandyang/p/4475985.html

https://www.jianshu.com/p/c82cada7e5b0

https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes30.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值