回忆一下动态规划
我的理解就是动态规划实际上就是递推,他的时间复杂度一般比暴力法,递归法都要低,原因是因为动态规划是利用记忆好的历史数据,(不重复记忆)并使用
下面我将动态规划的算法思路,随解题过程来梳理回亿
题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”
示例 3:
输入:s = “a”
输出:“a”
示例 4:
输入:s = “ac”
输出:“a”
思路
1. 确定状态
判断某一子串是否为回文子串,我们以dp[i][j]来表示,从i~j这段子串,是否为回文子串(确定状态这一步需要经验(刷题数量),需要悟性)
2. 初始化
我们直到当只有一个字符时就是回文子串
for(int i=0;i<len;i++){
dp[i][i]=true;//初始化
}
3. 确定转移方程
确定转移方程我们一般由一个子问题来确定,且遵循后无效性原则
对于一个子串,若为回文子串,首先子串头与尾相同,依次以第n个与倒数第n个字符相同这种形式递推即可,
那么递推方程就显而易见了
dp[i][j]=dp[i+1][j-1];
4. 处理边界情况
当我们比对的子串只有2个或3个字符时
再进行i+1,j-1我们将会越界,所以我们需要处理边界情况
即当字符只有2个或3个时
只要满足
arr[i]==arr[j],dp[i][j]就为true
if(j-i<3){
dp[i][j]=true;
}
(最后设置一个最大值,截取这段最大值即可)
动态规划代码
class Solution {
public String longestPalindrome(String s) {
int len=s.length();
char[] arr=s.toCharArray();
boolean dp[][]=new boolean[len][len];//表示从第i个到第j个字符之间的字符串是否为回文子串
//处理特殊情况
if(len<=1){
return s;
}
//初始化
for(int i=0;i<len;i++){
dp[i][i]=true;//自身与自身相等
}
int max=1;
int begin=0;
int end=0;
for(int j=1;j<len;j++){
for(int i=0;i<len-1&&i<j;i++){//注意处理下标之间的关系
if(arr[i]!=arr[j]){
dp[i][j]=false;
}else{
if(j-i<3){
dp[i][j]=true;
}else{
dp[i][j]=dp[i+1][j-1];//如果i在外层,则i+1没有初始化,递推式不成立
}
}
if(j-i+1>max && dp[i][j]){//还要满足状态对应
max=j-i+1;
begin=i;
end=j;
}
}
}
return s.substring(begin,end+1);
}
}
动态规划注意事项
在我们书写递推式的时候
dp[i][j]=dp[i+1][j-1]
我们应要考虑到i或j在for循环的里外层
如果i在外层,递推的时候dp[i+1]并未初始化,故应当将j放在外层
暴力代码
全排列再自定义函数传入判断
class Solution {
public String longestPalindrome(String s) {
int begin=0;
int end=0;
int maxlength=0;//初始化为1??
char[] arr=s.toCharArray();
if(arr.length<2){
return s;
}
for(int i=0;i<arr.length;i++){//越界?
for(int j=i+1;j<arr.length;j++){
if(issubstring(arr,i,j)&&j-i+1>maxlength){
begin=i;
end=j;
maxlength=j-i+1;
}
}
}
return s.substring(begin,end+1);
}
private boolean issubstring(char[]arr,int begin,int end){
while(begin<end){
if(arr[begin]!=arr[end]){
return false;
}
begin++;
end--;
}
return true;
}
}