疑惑:
疑问1:f[i][j]为何不用初始化? 在这里必定会满足一个子序列为回文串(最小值最坏情况下为1)。所以可以用默认值0表示还没求过
疑问2: 为何不像石子合并枚举每种最后一种情况?
因为表示含义不一样,他这表示的是满足回文序列的长度f[l,r]包含了f[l+1,r]、f[l,r-1]、f[l+1,r-1]…等多种情况
Scanner scanner=new Scanner(System.in);
int N=1010;
String string=scanner.next();
int n=string.length();
int[][] f=new int[N][N];//表示[l,r]之间的所有回文子序列的集合,属性是最大回文子序列的长度
//区间dp一般是枚举长度,再枚举左端点,再以左求右。
//若是左右端点从小到大循环(即两重循环):枚举l=1,r=3时,计算f[l][r]可能会用到f[2][3],然而f[2][3]还没算呢
//该集合根据四种情况来划分:1.包含l、r;2.包含l不含r;3.不包含l包含r;4.不包含lr;
//其中23无法用集合表示,那么可以用更大范围的即包含这23情况的,这里有重复计算但是没关系他求的是最大值
//不是数量。eg:max(2,3)和max(1,3)得出结果还是3,虽然3算了两次
for(int len=1;len<=n;len++) {
for(int l=0;l+len-1<n;l++) {//若右端点没越界
int r=l+len-1;
if(l==r) {
f[l][r]=1;//长度为一的字符串是最小子序列,且为1
continue;
}
if(string.charAt(l)==string.charAt(r))//若左右端点相等
//这种情况就是包含左右端点的情况,长度是f[l+1][r-1](这存储的是l+1和r-1之间的长度最大的回文子序列情况+2(两个端点)
f[l][r]=f[l+1][r-1]+2;
//求最大值可以用更大的集合来表示某种情况
f[l][r]=Math.max(f[l][r], f[l][r-1]);//f[l][r-1]:包含了二种情况,即1.含s[l]不含s[r];2.不含s[l]不含s[r]
f[l][r]=Math.max(f[l][r], f[l+1][r]);//f[l+1][r]:包含了二种情况,即1.不含s[l]含s[r];2.不含s[l]不含s[r]
}
}
System.out.println(n-f[0][n-1]);//求最小脱落种子数等价于总长度减去最大公共子序列的长度