【LeetCode】最长回文子串【动态规划或中心扩展】
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-palindromic-substring
方法1:动态规划
最优子结构:当一个串是一个回文串的时候,在其头尾各加一个相同的字符组成的新字符串依旧是一个回文串
dp[i][j]=1,代表从下标i到下标j组成的字符串是一个回文串
如果s[i]==s[j]&&dp[i+1][j-1]==1,那么dp[i][j]=1
初始化:
1.对于一个字符的回文串:dp[i][i]=1
2.对于两个字符的回文串:如果s[i]==s[i+1],那么dp[i][i+1]=1
对于三个字符的回文串:如果s[i]==s[j]&&dp[i+1][j-1]==1,那么dp[i][j]=1
时间复杂度:O(N^2)
空间复杂度:O(N^2)
class Solution { public: string longestPalindrome(string s) { if(s.size()==0) return ""; if(s.size()==1) return s; if(s.size()==2&&s[0]==s[1]) return s; int n=s.length(); int dp[n+1][n+1]; memset(dp,0,sizeof(dp)); int ans=1; int start=0; for(int i=0;i<n;i++) { dp[i][i]=1; } for(int i=0;i<n-1;i++) { if(s[i]==s[i+1]) { dp[i][i+1]=1; ans=2; start=i; } } int l=3; while(l<=n) { for(int i=0;i<n-l+1;i++) { int j=i+l-1; if(dp[i+1][j-1]==1&&s[i]==s[j]) { dp[i][j]=1; start=i; ans=l; } } l++; } return s.substr(start,ans); } };
golang实现
func longestPalindrome(s string) string {
if len(s)==1 || (len(s)==2 && s[0]==s[1]){
return s
}
dp:=[1005][1005]int{} // dp[i][j]==1 代表 i到j是回文串
ans:=1
start:=0
// dp 初始化
for i:=0;i<len(s);i++{
dp[i][i]=1
}
for i:=0;i<len(s)-1;i++{
if s[i]==s[i+1]{
dp[i][i+1]=1
ans=2
start=i
}
}
// 利用最小子结构的性质 逐渐试探最长的回文串
l:=3
for l<=len(s){
for i := 0; i < len(s)-l+1; i++ {
j:=i+l-1
if s[i]==s[j] && dp[i+1][j-1]==1{
dp[i][j]=1
ans=l
start=i
}
}
l++
}
return s[start:start+ans]
}
方法2:中心扩展法
将每个字符当作回文串的中心,然后往两边扩展,取扩展得到的回文串的最大值就是最长回文子串
考虑到回文串的奇偶情况,我们可以算两种情况:以一个字符为回文串的中心,以两个字符为回文串的中心,然后去二者最大值就可以
时间复杂度:O(N^2),以每个字符为中心需要遍历一次,然后每次都需要往两边扩展
空间复杂度:O(1),只需要用到一些常量
class Solution { public: int f1(string str,int left,int right,int n) { int c=1; //cout<<"left="<<left<<" right="<<right<<" n="<<n<<endl; left--; right++; while(left>=0&&right<n&&str[left]==str[right]) { left--; right++; c+=2; } return c; } int f2(string str,int left,int right,int n) { int c=0; if(str[left]==str[right]) c=2; else { c=1; return c; } left--; right++; while(left>=0&&right<n&&str[left]==str[right]) { left--; right++; c+=2; } return c; } string longestPalindrome(string s) { int n=s.size(); if(n==0) return ""; if(n==1) return s; if(n==2&&s[0]==s[1]) return s; int ans=1; int start=0; for(int i=0;i<n-1;i++) { int x1=f1(s,i,i,n); int x2=f2(s,i,i+1,n); //cout<<"i="<<i<<" x1="<< x1<<" x2="<<x2<<endl; if(max(x1,x2)>ans) { ans=max(x1,x2); //cout<<"ans="<<ans<<endl; if(ans%2==1) start=i-ans/2; else start=i-(ans-2)/2; //cout<<"start="<<start<<endl; } } return s.substr(start,ans); } };
golang实现
func work(s string, left, right int) (int,int,int) {
for left >= 0 && right < len(s) && s[left] == s[right] {
left--
right++
}
left++
right--
return left,right,right-left+1
}
func longestPalindrome(s string) string {
if len(s) == 1 || (len(s) == 2 && s[0] == s[1]) {
return s
}
start,end:=0,0
ans:=0
// 将每个字符当做回文串的中心,往两边扩展,取扩展得到的最大值就是最长的回文串
for i:=0;i<len(s);i++{
// 分别以一个和两个字符为回文串中心向两边扩展,结果取二者最大值
left1,right1,ans1:=work(s,i,i)
left2,right2,ans2:=work(s,i,i+1)
if ans1>ans{
start,end=left1,right1
ans=ans1
}
if ans2>ans{
start,end=left2,right2
ans=ans2
}
}
return s[start:end+1]
}
还有一个解决方案是马拉车算法
时间复杂度为O(N)!!!
但是我目前也没有掌握,就没有贴出来,怕误导别人
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
2018-08-02 POJ 1949 Chores (很难想到的dp)
2018-08-02 hdu 1520 Anniversary party(第一道树形dp)
2018-08-02 (CodeForces - 5C)Longest Regular Bracket Sequence(dp+栈)(最长连续括号模板)