【dp力扣】环绕字符串中唯一的子字符串

题目链接

题目:

java 复制代码
class Solution {
    public int findSubstringInWraproundString(String s) {

    }
}

审题:

给定base这个字符串,它是"abcd......xyz"围成的一个环形无线长度的字符串(只有小写的26个字母)。

在给定一个字符串s,题目要求计算:在s串中的所有的子串(至少包含一个字符)中,有多少个子串在base串中出现过

示例:

细节注意:

在示例二中,我们要知道重复出现的字符我们只记录一次!!!

尽管'cac'中两个c都存在于base串中,但是只算一个。

状态表示:

dpi

i表示,以0,i为范围,i位置为结尾,所有子数组在base串中出现的总次数。

java 复制代码
class Solution {
    public int findSubstringInWraproundString(String s) {
         
            
             //把字符串变成字符数组,等一下好操作。
             char[] sArr=s.toCharArray(); 
 
                   int n=sArr.length;


            //dp[i]表示,以[0,i]为范围,i位置为结尾,所有子数组在base串中出现的总次数。
            int[] dp=new int[n];

          
    }
}

通过dp计算正确答案(先讲)

这里我们先不推导状态方程,暂且假设dp数组已经被我们填表完成了。

依据这个dp数组,如何求出答案所需要的子数组出现的总次数呢?

一些童鞋可能会直接说dp数组求和即可。按照经验的确如此,刚开始我也是这样想的。

但,事出反常必有妖。

当前这个问题是解决这个算法的重要环节,所以我放到这里讲。

刚才小编在示例处强调过,重复的字符只能计算一次!所以如果直接对dp求和,那么结果只能是>=正确答案(因为加上了重复的次数)。

比如,s="abdb",在dp数组中dp1dp和dp3(dpi含义,请看上面代码注释)两个值一定有重复的计数,因为"ab"和"abdb"中都有b这个字符。因此如果是简单的dp求和,结果可能就会比正确答案大。

如何避免重复计数的情况发生呢?(注意下面的内容第一次听可能有点绕,但不难,细心听几遍就懂了)

可以使用哈希的思想避免重复计数。

题目提示说了,s和base一样都是小写的字母,所以定义一个哈希表:

java 复制代码
class Solution {
    public int findSubstringInWraproundString(String s) {
         
            
             //把字符串变成字符数组,等一下好操作。
             char[] sArr=s.toCharArray(); 
 
                   int n=sArr.length;


            //dp[i]表示,以[0,i]为范围,i位置为结尾,所有子数组在base串中出现的总次数。
            int[] dp=new int[n];

            这里假定dp数组已经填表完毕了!!



            //确定返回值(用于去重)
            int[] hash=new int[26];

 
    }
}

把dp表映射到hash表中,hash求和就是答案了(映射前hash的每一个元素都是0)。

那么dp表怎么映射到hash表中呢?

实际上,借助字符数组sArr即可:

hash0对应的是sArri=='a'

hash1对应的是sArr\[j=='b'

所以:

hash0=sArri

hash1=sArrj

不过,以上这种模式并没有实现去重。

如果sArri和sArrj都等于'b',怎么办?

这实际上就回到了刚才的"abdb"中,dp1和dp3出现重复计数的问题了。

其实很简单,直接取最大值:max(dp1,dp3)即可。

为什么呢?

dpi表示,以0,i为范围,i位置为结尾,所有子数组在base串中出现的总次数。

而dp3实际上已经包含了子数组dp1中的所有情况,

也就是说如果i<j,si==sj,那么一定有:dpi<dpj!(细细理解这句话)

java 复制代码
class Solution {
    public int findSubstringInWraproundString(String s) {
         
            
             //把字符串变成字符数组,等一下好操作。
             char[] sArr=s.toCharArray(); 
 
                   int n=sArr.length;


            //dp[i]表示,以[0,i]为范围,i位置为结尾,所有子数组在base串中出现的总次数。
            int[] dp=new int[n];

            这里假定dp数组已经填表完毕了!!

    

            //确定返回值
            int[] hash=new int[26];

                //开始把dp映射到hash(用于去重)
            for(int i=0;i<n;i++){
                hash[sArr[i]-'a']=Math.max(hash[sArr[i]-'a'],dp[i]);
            }

            //hash求和就是答案
            int sum=0;
            for(int h:hash){
                sum+=h;
            }

        return sum;
    }
}

状态方程的初始化以及推导

在回顾以下状态表示dpii表示,以0,i为范围,i位置为结尾,所有子数组在base串中出现的总次数。

所以dpi和dpi-1有什么关系呢?

倘若两个字符si和si-1是连续的,即si-1+1!=si(ASCII码)并且si-1!='z'且si!='a'。那么dpi=dpi-1+1。(以i为结尾,实际上,也就增加了si这一个字母)

反之,如果不满足粗体条件,dpi=1。

如下AC代码:

java 复制代码
class Solution {
    public int findSubstringInWraproundString(String s) {
         
            
             //把字符串变成字符数组,等一下好操作。
             char[] sArr=s.toCharArray(); 
 
                   int n=sArr.length;


            //dp[i]表示,以[0,i]为范围,i位置为结尾,所有子数组在base串中出现的总次数。
            int[] dp=new int[n];

            这里假定dp数组已经填表完毕了!!

            dp[0]=1;//涉及i-1,只需要把这个初始化即可!!!!
            for(int i=1;i<n;i++){
                if(sArr[i-1]+1==sArr[i]||(sArr[i-1]=='z'&&sArr[i]=='a')){
                        dp[i]=dp[i-1]+1;
                    }
                else dp[i]=1;
            }

            //确定返回值
            int[] hash=new int[26];

                //开始把dp映射到hash(用于去重)
            for(int i=0;i<n;i++){
                hash[sArr[i]-'a']=Math.max(hash[sArr[i]-'a'],dp[i]);
            }

            //hash求和就是答案
            int sum=0;
            for(int h:hash){
                sum+=h;
            }

        return sum;
    }
}
相关推荐
Navigator_Z10 分钟前
LeetCode //C - 1252. Cells with Odd Values in a Matrix
c语言·算法·leetcode
语戚2 小时前
力扣 1621. 大小为K的不重叠线段的数目:动态规划(Java 实现)
java·算法·leetcode·动态规划·力扣·dp
青山木2 小时前
Hot 100 --- 最长有效括号
java·数据结构·算法·leetcode·动态规划
这料鬼有毒3 小时前
二刷hot100-1143.最长公共子序列
算法·leetcode·动态规划
政企项目老覃4 小时前
边缘 AI 推理部署:安防零售场景下的模型裁剪与端侧落地实践
人工智能·程序人生·算法·性能优化·vllm
GreenTea6 小时前
发布 3 天登顶 HN:不生成一个字的模型 Jev,我把它的源码和黑料都扒了一遍
前端·后端·算法
庖丁解牛6 小时前
无穷小不是一个“神秘小数”,而是一场奔向 0 的过程
算法
ZPC82107 小时前
YOLO26 识别串番茄果梗
人工智能·深度学习·算法·机器学习
景熙55237 小时前
14.Java 集合框架从入门到源码万字详解(含 ArrayList/LinkedList/HashMap 源码、泛型通配符、红黑树)
java·开发语言·数据结构·算法
aaaameliaaa7 小时前
结构体 结构体
c语言·笔记·算法