【Algorithm】GPLT L3-020 至多删三个字符

该博客讨论了一种算法问题,即如何计算一个字符串在最多删除三个字符后的不同子串数量。通过动态规划和去重策略解决此问题,代码实现使用C++,并详细解释了算法逻辑和去重的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

L3-020 至多删三个字符

 

dp[i][j]  表示前 i 个字符删掉 j 个字符结果有 dp[i][j] 个。

dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1]

  1. 第 i 个字符不删,前 i - 1 个字符删除掉 j 个再加上当前字符能取到的个数。
  2. 第 i 个字符删了,前 i - 1 个字符删除掉 j - 1 个能取到的个数。

这样会有重复的。例如一个字符串 abcdefdxyz,删除 def 和删除 efd 后得到的字符串都是abcdxyz,这时候就要去重了。

根据上面那个例子,可以发现对于一个字符 s[ i ],如果在 i 之前存在一个 last,使得 s[ last ] = s[ i ],那么删除 [ last, i - 1 ] 间的字符和删除 [ last + 1, i ] 间的字符其实是重复的。

那么 dp[ i ][ j ] 就要减去这个重复,这个重复可表示为 dp[ last - 1 ][ j - ( i - last ) ]。当然,如果 i 与last 的差大于等于目前的 j,就已经不可能受到影响了。

abcdefdxyz

abcdefdxyz

删去左部分和右部分的情况重复,那么就需要去重。

首先,如果我们已经决定要删去左部分,删除个数为 i - last 个,还需在前 last - 1 个字符种删除 j - (i - last) 个,即 dp[ last - 1 ][ j - ( i - last ) ]。

选择上述去重,而不是选择右部分 dp[ last ][ j - ( i - last ) ],是因为左部分是已经去重过了,而右部分还存在重复的影响。

#include <iostream>
#include <cstring>

using namespace std;

typedef long long LL;
const int N = 1e6 + 10;

char s[N];
LL dp[N][4];
int vis[30];

int main()
{
    scanf("%s", s + 1);
    
    int len = strlen(s + 1), last;
    
    for (int i = 0; i <= len; i++) dp[i][0] = 1;
    
    for (int i = 1; i <= len; i++) {
        
        last = vis[s[i] - 'a'];
        vis[s[i] - 'a'] = i;
        
        for (int j = 1; j <= 3; j++) {
            dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
            if (last && j - (i - last) >= 0)
                dp[i][j] -= dp[last][j - (i - last)];
        }
    }
    
    printf("%lld\n", dp[len][0] + dp[len][1] + dp[len][2] + dp[len][3]);
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值