P9753 [CSP-S 2023] 消消乐

写一种考场上想到的线性做法。

根据性质,若 s[i,j−1]s[i,j−1] 与 s[j,k]s[j,k] 均为合法子串,那么 s[i,k]s[i,k] 必为合法子串。

考虑 dp,设 fifi​ 表示以第 ii 位作为结尾的合法子串数量,容易得出转移方程:fi=fgi−1+1fi​=fgi​−1​+1,其中 gigi​ 表示最大且能使 s[gi,i]s[gi​,i] 成为合法子串的下标。答案即为 ∑i=1nfi∑i=1n​fi​。

由于 gigi​ 是满足条件中最大的,因此必有 si=sgisi​=sgi​​,我们就可以按下图的方式从 ii 往前跳,初始时 gi=i−1gi​=i−1,之后不断令 gi→ggi−1gi​→ggi​​−1,直到满足 si=sgisi​=sgi​​。

1

但这样会被形如 abbccddeea 之类的数据退化到 n2n2 级别,因此考虑如何优化。

如果令 hi=gi−1hi​=gi​−1,会发现跳的方式变成了这样,形成了若干条链,由此在每个位置记录 ai,cai,c​,表示 sai,c+1=csai,c​+1​=c 且能使 [ai,c+1,i][ai,c​+1,i] 成为合法子串的最大的下标。

1

每次修改的值只有 ai,siai,si​​,我们便可以用 toitoi​ 表示该链的链头,每次修改 atoi,siatoi​,si​​ 即可。

时间复杂度:O(n)O(n)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e6+5;
int n,dp[N],a[N][26],to[N];
char s[N];
ll ans;
int main()
{
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++)
    {
        to[i]=i;
        int x=a[to[i-1]][s[i]-'a'];
        if(x) to[i]=to[x-1],dp[i]=dp[x-1]+1;
        a[to[i]][s[i]-'a']=i,ans+=dp[i];
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值