用一道经典的回文子串题来讲解这两种方法
https://leetcode.cn/problems/longest-palindromic-substring/description/
1.动态规划
时间复杂度 n的平方
空间复杂度 n的平方
1.状态表示
用dp[i][j]表示所有的状态,表示从i - j的字符串是否为回文串
可以看出我们只需要用到半边的dp表
2.状态转移方程
先根据i位置和j位置的字符是否相等来分类
如果相等还要分一个字符两个字符来判断
以下是三种分类的图解
3.初始化
这里无需初始化
由于会发现越界的只有第三种情况,但是第三种情况可能发生越界的时候都被我们特判了
在我们的状态表示中有一个隐含条件(i <= j)
会发生越界的两种情况,比如当i = j的时候 i + 1 并且 j - 1的时候 i 在 j 的前面,发生越界了,但是我们已经特判了,i + 1 = j 也是一样的道理
4.填表顺序
从dp[i + 1][j - 1]可以看出
从下往上填,从左往右填表
5.返回值
由于每道题返回的都不一样,这一题是求最大长度,我们用len标记最大长度,用begin标记左边的边界,遍历一遍我们的dp表即可
代码
class Solution {
public String longestPalindrome(String ss) {
char[] s = ss.toCharArray();
int n = s.length;
boolean[][] dp = new boolean[n][n];
for(int i = n - 1;i >= 0;i--){
for (int j = i; j < n; j++) {
if(s[i] == s[j]){
dp[i][j] = i + 1 < j ? dp[i + 1][j - 1] : true;
}
}
}
int len = 0;
int begin = 0;
for(int i = n - 1;i >= 0;i--){
for (int j = i; j < n; j++) {
if(dp[i][j]){
if(len < j - i + 1){
begin = i;
len = j - i + 1;
}
}
}
}
return ss.substring(begin,begin + len);
}
}
2.中央扩展算法
时间复杂度 n的平方
空间复杂度 1
算法原理
这个算法比较简单通过枚举每个字符左右两边是否存在相同的字符来判断长度
但是注意奇数和偶数的区别,aba和aa,所以我们也要判断偶数
1) 先让两个指针指向同一个字符,往两边扩展,如果两边字符相等,继续扩展,直到停止
2) 然后再让right指向a右边的位置,判断偶数,同理
细节!!!
我们还是采取len和begin来标记,但是每次扩展的长度一定要注意
len = right - left - 1
这里我建议直接举例子 3 1 2 1 4
下标 0 1 2 3 4
在2扩展的时候最终left和right 会指向3和4
我们要求的是1 2 1 的长度,所以(right - 1) - (left + 1) + 1才是长度
代码
class Solution {
public String longestPalindrome(String ss) {
//中心扩展算法
int n = ss.length();
int begin = 0;
int len = 0;
for(int i = 0;i < n;i++){
int left = i;
int right = i;
while(left >= 0 && right < n && ss.charAt(left) == ss.charAt(right)){
left--;
right++;
}
if(len < right - left - 1){
len = right - left - 1;
begin = left + 1;
}
left = i;
right = i + 1;
while(left >= 0 && right < n && ss.charAt(left) == ss.charAt(right)){
left--;
right++;
}
if(len < right - left - 1){
len = right - left - 1;
begin = left + 1;
}
}
return ss.substring(begin,begin + len);
}
}
总结
动态规划虽然看起来不如中央扩展,但是也有他的好处,可以统计所有的情况,在某些题目会有奇效比如https://leetcode.cn/problems/palindrome-partitioning-iv/description/