『题目』:
指定一个字符串 s s s,你可以从中删除一些字符,使得剩下的串是一个回文串。如何删除才能使得回文串最长呢?输出需要删除得字符个数。
『题解』:
因为是回文串,所以一种官方题解是将字符串反转,然后求最长公共子序列,那么就可以知道哪些字符可以删除。然后下面是我自己的想法,首先同样都要将字符串反转,然后类似枚举括号一样,先从最大的位置开始枚举然后进行DP,递推公式(其中
i
i
i 是当前串的第
i
i
i 个字符,
j
j
j 是当前串的第
j
j
j 个字符)
{
t
[
i
]
[
j
]
=
m
a
x
(
2
+
t
[
i
−
1
]
[
j
−
1
]
,
t
[
i
]
[
j
−
1
]
,
t
[
i
−
1
]
[
j
]
)
i
f
i
≠
j
t
[
i
]
[
j
]
=
m
a
x
(
1
+
t
[
i
−
1
]
[
j
−
1
]
,
t
[
i
]
[
j
−
1
]
,
t
[
i
−
1
]
[
j
]
)
i
f
i
=
j
t
[
i
]
[
j
]
=
m
a
x
(
t
[
i
−
1
]
[
j
−
1
]
,
t
[
i
]
[
j
−
1
]
,
t
[
i
−
1
]
[
j
]
)
i
f
s
[
i
]
≠
s
[
j
]
\begin{cases} t[i][j] = max(2+t[i-1][j-1],t[i][j-1],t[i-1][j]) \quad if\ i \neq j \\ t[i][j] = max(1+t[i-1][j-1],t[i][j-1],t[i-1][j]) \quad if\ i = j \\ t[i][j] = max(t[i-1][j-1],t[i][j-1],t[i-1][j]) \quad if\ s[i] \ne s[j] \\ \end{cases}
⎩⎪⎨⎪⎧t[i][j]=max(2+t[i−1][j−1],t[i][j−1],t[i−1][j])if i=jt[i][j]=max(1+t[i−1][j−1],t[i][j−1],t[i−1][j])if i=jt[i][j]=max(t[i−1][j−1],t[i][j−1],t[i−1][j])if s[i]=s[j]
也就是说,我们先从最前的字符找到最远匹配的字符,即使期间也有其他匹配的字符,当然这个括号越大越好,如果当前的字符不能匹配,但是上一个匹配的位置
(
i
−
1
,
j
)
,
(
i
,
j
−
1
)
,
(
i
−
1
,
j
−
1
)
(i-1,j),(i,j-1),(i-1,j-1)
(i−1,j),(i,j−1),(i−1,j−1)可能就已经匹配了,那么也算是对于当前字符来说已经有匹配。要注意,一个字符最多和一个字符匹配,可能说得不太清楚,举个例子
a
c
d
b
c
a
a\ c\ d\ b\ c\ a
a c d b c a。
a
c
b
d
c
a
a
2
2
2
2
2
2
c
2
4
4
4
4
0
d
2
4
4
5
0
0
b
2
4
5
0
0
0
c
2
4
0
0
0
0
a
2
0
0
0
0
0
\begin{array}{c|llllll} {}&{a}&{c}&{b}&{d}&{c}&{a}\\ \hline {a}&{2}&{2}&{2}&{2}&{2}&{2}\\ {c}&{2}&{4}&{4}&{4}&{4}&{0}\\ {d}&{2}&{4}&{4}&{5}&{0}&{0}\\ {b}&{2}&{4}&{5}&{0}&{0}&{0}\\ {c}&{2}&{4}&{0}&{0}&{0}&{0}\\ {a}&{2}&{0}&{0}&{0}&{0}&{0}\\ \end{array}
acdbcaa222222c244440b244500d245000c240000a200000
例如,从轴向看,第一个字符a
从后也能找一个字符匹配,而且位置不相同,所以最长的回文串为2
,接着匹配到c
,可以这个字符和a
并不匹配,但是因为在上一个位置的范围比它大,而且已经有匹配,所以这个位置也写2
,以此类推,可以尝试着手画一图,就可以推出思想。
『注意』:
该想法没有在OJ上测试过,若有错误请指点
『实现』:
import java.util.Scanner;
/**
* Author:
* Gavinjou大笨象
* Date:
* 2019-05-01
* Description:
* DP题
*/
public class huiwenOn2 {
//读入字符串
private static char[] a;
//数组大小
private static int[][] num;
public static void main(String[] args) {
int ans = 0;
Scanner in = new Scanner(System.in);
String input = in.nextLine();
String [] stmp = input.split(" ");
a = new char[stmp.length];
//分割字符串
for (int i = 0;i < stmp.length;i++)
{
a[i] = stmp[i].charAt(0);
}
//申请空间,借位1,就可以不用判断语句,直接用公式推导
num = new int[a.length + 1][a.length + 1];
for(int i = 1; i <= a.length; i++)
{
//计算字符串的最大需要遍历的位置,无需全遍历,从左到右即可
int tmp = a.length - i + 1;
for(int j = 1; j <= tmp; j++)
{
//正向索引字符串
int indexi = i - 1;
//反向索引字符串
int indexj = a.length - j;
int addnum = 0;
//公式
if(a[indexi] == a[indexj])
{
if(indexi == indexj) addnum = 1;
else addnum = 2;
}
num[i][j] = Math.max(addnum + num[i-1][j-1],num[i][j-1]);
num[i][j] = Math.max(num[i][j],num[i-1][j]);
}
//判断最大括号匹配
ans = Math.max(num[i][tmp],ans);
}
//输出要删的字符串
System.out.println(a.length-ans);
}
}