两种思路求解——Longest Palindromic Substring (最长回文子串)_C++

【链接】https://leetcode-cn.com/problems/longest-palindromic-substring/

题目:

Given a string s, return the longest palindromic substring in s.

Example 1:

Input: s = "babad"
Output: "bab"
Note: "aba" is also a valid answer.

Example 2:

Input: s = "cbbd"
Output: "bb"

Example 3:

Input: s = "a"
Output: "a"

Example 4:

Input: s = "ac"
Output: "a"

Constraints:

1 <= s.length <= 1000
s consist of only digits and English letters (lower-case and/or upper-case),


思路

1,动态规划,DP数组。时间复杂度O(n^2),空间复杂度O(n^2)。

网上遍地使用的DP方程组:

其思想核心是,如果区间A满足条件,则区间A内必存在至少一个子区间满足条件。对于字符串str,假设dp[i,j]=1表示str[i...j]是回文子串,那个必定存在dp[i+1,j-1]=1。这样最长回文子串就能分解成一系列子问题,可以利用动态规划求解了。状态转移方程如上图所示。

上面的状态转移方程表示,当str[i]=str[j]时,如果str[i+1...j-1]是回文串,则str[i...j]也是回文串;如果str[i+1...j-1]不是回文串,则str[i...j]不是回文串。

      初始状态

  • dp[i][i]=1
  • dp[i][i+1]=1 if str[i]==str[i+1]

2,Manacher法,线性算法,时间复杂度O(n)

其思想是:

1,对于每一个元素,我们都认为其是某个回文子串的中心,我们同时向两边伸展,然后取其中的最大值。

步骤:

1,为解决奇偶长度不相同带来的计算问题。先将数组转化为奇数长度,在字符串S的字符之间和S的首尾都插入一个“#”,如:S=“abba”变为T="#a#b#b#a#" 。

2,在新的数组中,求出以各个元素为中心的最长回文半径数组P[]。

3,由于在插入新的字符“#”后,以i为中心的回文子串的长度变为2P[i]-1,而实际原字符串的长度为P[i]-1。

 

代码

1,动态规划

  • dp[i][j] 表示从s[i]到s[j]是否是回文
  • dp[i][i] = true 因为dp[i][i]一定是回文

双循环写法(好理解):

string longestPalindrome(string s)  
{
    int size = s.size();
	vector<vector<bool>> dp(size,vector<bool>(size,false));
	int start = 0;
	int maxLength = 1;
	for (int i = 0; i < size; i++)
	{
		for (int j = 0; j <=i; j++)
		{
			if (i - j < 2)
			{
				dp[j][i] = (s[j]==s[i]);
			}
			else if (i - j >= 2)
			{
				dp[j][i] = (s[j] == s[i]) && (dp[j+1][i-1]);
			}
			if (dp[j][i] && maxLength < i - j + 1)
			{
				maxLength = i - j + 1;
				start = j;
			}
		}
	}
	return s.substr(start,maxLength);

}

先计算长度=2的子串,再进行扩展:

string longestPalindrome(string s)
{
    if (s.empty()) return "";
    int len = s.size();
    if (len == 1)return s;
    int longest = 1;
    int start=0;
    //建立动态数组dp
    vector<vector<int> > dp(len,vector<int>(len));
    //求出相邻两个元素间是否是回文串
    for (int i = 0; i < len; i++)
    {
        dp[i][i] = 1;
        if(i<len-1)
        {
            if (s[i] == s[i + 1])
            {
                dp[i][i + 1] = 1;
                start=i;
                longest=2;
            }
        }
    }
    //再逐渐扩展长度,计算各个子串是否为回文串
    for (int l = 3; l <= len; l++)//子串长度
    {
        for (int i = 0; i+l-1 < len; i++)//枚举子串的起始点
        {
            int j=l+i-1;//终点
            if (s[i] == s[j] && dp[i+1][j-1]==1)
            {
                dp[i][j] = 1;
                start=i;
                longest = l;
            }
        }
    }
    return s.substr(start,longest);
}

2,Manacher法

string longestPalindrome(string s)
{
    //在开头加入‘$’是为了防止访问越界
    string newStr = "$#";
    //加入‘#’
	for (int i = 0; i < s.length(); i++)
	{
		newStr += s[i];
		newStr += "#";
	}
    //以i为中心的回文串半径数组
	vector<int> Len(newStr.length(), 0);
	int pos = 0, mx = 0;
	int start = 0, maxLen = 0;
	for (int i = 1; i < newStr.size(); i++)
	{
		Len[i] = i < mx ? min(Len[2 * pos - i], mx - i) : 1;
		while (i + Len[i] < newStr.size() && i - Len[i]>0 && newStr[i + Len[i]] == newStr[i - Len[i]])
			Len[i]++;
		if (i + Len[i] > mx) //如果新计算的最右侧端点大于mx,则更新pos和mx
		{
			pos = i;
			mx = i + Len[i];
		}
		if (Len[i] - 1 > maxLen)
		{
			start = (i - Len[i]) / 2;
			maxLen = Len[i] - 1;
		}
	}
	return s.substr(start, maxLen);
    
}

参考:

https://www.cnblogs.com/love-yh/p/7072161.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聊聊技术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值