动态规划,本质上就是 带备忘录的递归的计算过程。递归过程为 自顶向下的问题拆解和自底向上的问题计算。迭代形式的动态规划,略过问题拆解,直接计算,能节省一定的时空。
本题中,因递归法的时空效率太差(击败5%),需进一步优化(改为迭代形式)。
分析过程:
- 递归过程(暴力递归穷举 + 备忘录优化)
- 构造递归遍历(二叉树遍历框架)。构造递归,就是发现最优子结构的过程。对于最长回文子串,我们在构造递归时,关注怎么遍历所有子串。然后再判断子串性质。最后再择优。
- 画递归树,发现重叠子问题。
- 构造备忘录,消除重叠子问题的重复计算。
- 改写递归为迭代,提升效率。即将带备忘录的递归过程 改写为 迭代形式的动态规划过程。
- 明确备忘录的初始状态。从base case开始推。
- 明确状态转移的方法(递推式)。
- 递推填表。这里涉及到二维状态表的遍历方式。
class Solution {
public:
pair<int, int> ans;
string longestPalindrome(string s) {
int n = s.size();
vector<vector<int>> m(n, vector<int>(n, -1));
dfs(s, 0, n-1, m);
return s.substr(ans.first, ans.second - ans.first+1);
}
int isPalindrome(string& s, int i, int j, vector<vector<int>>& m) {
if (i>=j) return 1;
if(m[i][j] != -1) return m[i][j];
int ret;
ret = (s[i]==s[j] && isPalindrome(s, i+1, j-1, m)) ? 1:0;
m[i][j] = ret;
return ret;
}
void dfs(string& s, int i, int j, vector<vector<int>>& m) {
if(i==j) return;
if(m[i][j] != -1) return;
// if (ans.second-ans.first > j-i) return;
if (isPalindrome(s, i, j, m)) {
update_ans(i, j);
}
dfs(s, i+1, j, m);
dfs(s, i, j-1, m);
}
void update_ans(int i, int j) {
if (ans.second - ans.first < j-i) {
ans.first = i;
ans.second = j;
}
// cout<<ans.first<<","<<ans.second<<endl;
}
};