首先这个题目我自己是做错了的,最后AC是0.1,和没有一样(香菇…),但是思路上是对的,只是这应该是个动态规划问题,我做成了BFS的问题了。
动态规划
题意
首先定义非递减字符串,然后给n个非递减字符串,选择任意个拼起来,问能拼出来的最长非递减字符串长度
这个题目我们的关键点就在于怎么去存储每个非递减字符串,然后接下来的问题就死如何去拼接这些字符串。首先说一下存储的问题
存储字符串
存储字符串我们使用的是一个二维的dp数组,数组大小为dp[26][26]
,相信看到大小能知道每个元素代表什么了,没错dp[i][j]
就是代表的字母a+i
到和字母a+j
之间有多少个字符。假如我们现在有一个字符串"fhik"
,那么对应于我们dp数组中就是dp[5][10]=4
,表明目前为止给出的字符串中,在字母f
到k
中包含有4个字符。为什么是目前为止,因为假设我们还有一个字符串"fghik"
,那么我们是需要更新此处的值变为dp[5][10] = Math.max(dp[5][10], "fghik".length())
,表示因为有别的字符串,所以这中间的长度是变大了。这个地方要注意的是,前面的字符串"fhik"
其实就没有记住它的必要了,因为但凡我们需要区间[f, k]
内的长度,都是取此区间内那个较大的。
当然还有一个地方要注意的是,如果是一串相同的字符组成的字符串例如"dddd"
,那么对应的数组元素应该是dp[3][3] = 4
。
拼接字符串
好了,我们已经把那些字符串存储起来了,此时我们得到了什么呢,我们得到了这些字符串中每任意两个字符间的最大距离,或者说两个字符区间内的最多字符个数。例如还是上面的,我们得到了字符f
和k
之间的最多字符个数是5,还可能得到字符a
和h
之间的最多字符个数是6…。
于是我们进行拼接,其实拼接的过程也不难,就是我们申请一个数组result[26]
,每个数组的元素result[i]
代表从字符a
到字符a+i
之间最多的字符个数。那么这个result
数组如何获得呢,就是利用我们上面存储的所有字符串来获得。
假设我们相求dp[10]
,那么我们此时应该怎么做呢?要注意我们此时求的是在区间[a,k]
内的字符个数。那么我们就从其中的每个字符开始,然后求每个字符都到k
的最多字符个数,等式为result[i] = Math.max(result[i], result[j]+dp[j,i])
,其中j属于[0,i-1]
。这个式子就是相当于我拼接了两个字符串区间[x, j]
和[j, i)
当然x
是多少我们不知道,但我们知道result[j]
就是前面区间的最多字符个数了,然后再加上dp[j, i)
表示此区间内最多的字符串个数,于是我们就能求得区间[x, i)
内最多字符的个数了。当然能注意到我们右侧是个开区间,因为前面提到会有一串字符都相同的情况,此时我们再加上这个长度,即result[i] += dp[i][i]
。所以我们相求区间[0, i]
内的字符长度需要三个部分:
result[j]
:代表中间某一个部分的长度dp[j, i)
:代表此区间内的长度dp[i,i]
:代表仅由此字符构成的字符串长度
完整代码如下:
public class Main {
static int n;
static class Node {
char first; //存储一个字符串的开头
char last; //存储一个字符串的结尾
int len; //存储字符串的长度
}
static int[][] g = new int[26][26]; //我们解析中的dp数组
static int[] dp = new int[26]; //我们解析中的result数组
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
n = Integer.valueOf(scanner.nextLine());
for(int i = 0; i < n; ++i) {
Node state = new Node();
String cur = scanner.nextLine();
state.first = cur.charAt(0);
state.last = cur.charAt(cur.length()-1);
state.len = cur.length();
if(state.first == state.last) { //由同一字符组成的字符串,直接增加
g[state.first-'a'][state.last-'a'] += cur.length();
} else { //否则取一个较大值,例如"abfg"和"abcdefg"肯定是要同样区间内较大的
g[state.first-'a'][state.last-'a'] = Math.max(g[state.first-'a'][state.first-'a'], cur.length());
}
}
dp[0] = g[0][0]; //处理全为a的情况
for(int i = 1; i < 26; ++i) { //求区间[a,a+i]内的长度
int t = g[i][i];
for(int j = 0; j < i; ++j) {
//求区间[a,a+i]的较大值,例如["abc","ef"]和[“abcdef”]我们肯定要后者
dp[i] = Math.max(dp[i], dp[j] + g[j][i]);
}
dp[i] += t; //加上全是本字符的长度
}
int res = 0;
for(int i = 0; i < 26; ++i) { //得到了所有从字符a到字符a、b、c、d、e...的长度,求一个最大值
res = Math.max(res, dp[i]);
}
System.out.println(res);
}
}