阿里笔试2:求最长非递减字符长度

博客探讨了如何使用动态规划解决寻找最长非递减字符字符串的问题。通过存储字符串和拼接字符串的策略,解释了如何维护二维dp数组以记录字符间的最大距离,并详细阐述了拼接过程,包括如何计算不同字符区间内的最多字符个数。
摘要由CSDN通过智能技术生成

首先这个题目我自己是做错了的,最后AC是0.1,和没有一样(香菇…),但是思路上是对的,只是这应该是个动态规划问题,我做成了BFS的问题了。

动态规划

题意
首先定义非递减字符串,然后给n个非递减字符串,选择任意个拼起来,问能拼出来的最长非递减字符串长度

这个题目我们的关键点就在于怎么去存储每个非递减字符串,然后接下来的问题就死如何去拼接这些字符串。首先说一下存储的问题

存储字符串

存储字符串我们使用的是一个二维的dp数组,数组大小为dp[26][26],相信看到大小能知道每个元素代表什么了,没错dp[i][j]就是代表的字母a+i到和字母a+j之间有多少个字符。假如我们现在有一个字符串"fhik",那么对应于我们dp数组中就是dp[5][10]=4,表明目前为止给出的字符串中,在字母fk中包含有4个字符。为什么是目前为止,因为假设我们还有一个字符串"fghik",那么我们是需要更新此处的值变为dp[5][10] = Math.max(dp[5][10], "fghik".length()),表示因为有别的字符串,所以这中间的长度是变大了。这个地方要注意的是,前面的字符串"fhik"其实就没有记住它的必要了,因为但凡我们需要区间[f, k]内的长度,都是取此区间内那个较大的

当然还有一个地方要注意的是,如果是一串相同的字符组成的字符串例如"dddd",那么对应的数组元素应该是dp[3][3] = 4

拼接字符串

好了,我们已经把那些字符串存储起来了,此时我们得到了什么呢,我们得到了这些字符串中每任意两个字符间的最大距离,或者说两个字符区间内的最多字符个数。例如还是上面的,我们得到了字符fk之间的最多字符个数是5,还可能得到字符ah之间的最多字符个数是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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值