LeetCode:构造有效字符串的最少插入数

地址:. - 力扣(LeetCode)
题目描述:给你一个字符串 word ,你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次,返回使 word 有效 需要插入的最少字母数。
如果字符串可以由 “abc” 串联多次得到,则认为该字符串 有效

提示:

  • 1 <= word.length <= 50
  • word 仅由字母 “a”、“b” 和 “c” 组成。

题解

动态规划

d[i]表示前i个字符拼凑成若干个 abc所需要的最小插入数
d[0] = 0
如果word[i] 单独存在于一组 abc中,d[i]=d[i−1]+2
那怎么知道word[i]是不是单独存在一组abc中呢?

首先 我们能知道的是c>b>a
如果word[i]>word[i−1] 那就能肯定是在同一组abc中的
比如字符串ab,b>a 我们能肯定它们是在同一组abc中的
比如字符串ac,c>a 我们能肯定它们是在同一组abc中的
比如字符串bc,c>b 我们能肯定它们是在同一组abc中的

除了上述情况之外,其他的情况word[i]都是单独成一组的
比如ca,a<c,我们能肯定a就是单独成一组的,不可能跟前面的c成一组,因为这违背了abc这样的顺序
既然是单独成一组,就需要再插入两个元素构成一组 所以d[i]=d[i−1]+2

如果 word[i]>word[i−1],那么 word[i]可以和 word[i−1] 在同一组 abc 中,d[i]=d[i−1]−1
这种情况就是word[i]跟前面可以成一组,但是我们要知道的是在word[i]插入进word[0]-word[i-1]之前,我们通过动态规划,已经计算好了word[0]-word[i-1]这个字符串拼凑成若干个 abc所需要的最小插入数
那就是可以理解为,这个时候,word[0]-word[i-1]的最后三位字符串已经成组成abc了
这个时候我们把word[i]添加进去成组,也就意味着d[i-1],也就是word[0]-word[i-1]这个字符串拼凑成若干个 abc所需要的最小插入数需要减1,因为word[i]替换占位了,相应的插入数就要减少了,所以d[i]=d[i−1]−1

通过一个例子加深理解:
word=aacabc
答案应该是:3 a(bc)a(b)cabc

第一步:求d[1],因为a前面是空字符,所以字符串a肯定是自成一组的,d[1] = d[0]+2 = 2

第二步:求d[2],因为a前面是a,所以字符串word[1]:a自成一组的,d[2] = d[1]+2 = 4

第三步:求d[3],因为c前面是a,所以字符串word[2]:c跟前面的word[1]:a是同一组的,d[3] = d[2]-1 = 3

第四步:求d[4],因为a前面是c,所以字符串word[3]:a自成一组,d[4] = d[3]+2 = 5

第五步:求d[5],因为b前面是a,所以字符串word[4]:b跟前面的word[3]:a是同一组的,d[5] = d[4]-1 = 4

第六步:求d[6],因为c前面是b,所以字符串word[5]:c跟前面的word[4]:b是同一组的,d[6] = d[5]-1 = 3

所以答案就是d[6] = 3

代码实现
/**
 * @param {string} word
 * @return {number}
 */
var addMinimum = function(word) {
   const n = word.length
   const d = new Array(n+1).fill(0)
   for(let i=1;i<=n;i++){
       d[i] = d[i-1]+2
       if(i>1&&word[i-1]>word[i-2]){
            d[i] = d[i-1]-1
       }
   }
   
   return d[n]
};

状态转移时,最多只会依赖前一个状态 d[i−1],因此使用滚动数组优化可以使得空间复杂度降至 O(1)

/**
 * @param {string} word
 * @return {number}
 */
var addMinimum = function(word) {
   const n = word.length
   let d = 0
   for(let i=1;i<=n;i++){
       d+=2
       if(i>1&&word[i-1]>word[i-2]){

           // d = (d-2)-1  d-2是为了还原状态 因为前面+2了
           d-=3
       }
   }
   
   return d
};
  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值