1 条题解

  • 0
    @ 2025-8-24 22:51:31

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar SpadeA261
    **

    搬运于2025-08-24 22:51:31,当前版本为作者最后更新于2023-10-30 01:42:54,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


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

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

    考虑 dp,设 fif_i 表示以第 ii 位作为结尾的合法子串数量,容易得出转移方程:fi=fgi1+1f_i=f_{g_i-1}+1,其中 gig_i 表示最大且能使 s[gi,i]s[g_i,i] 成为合法子串的下标。答案即为 i=1nfi\sum_{i=1}^n f_i

    由于 gig_i 是满足条件中最大的,因此必有 si=sgis_i=s_{g_i},我们就可以按下图的方式从 ii 往前跳,初始时 gi=i1g_i=i-1,之后不断令 giggi1g_i\rightarrow g_{g_i}-1,直到满足 si=sgis_i=s_{g_i}

    1

    该做法的时间复杂度为 O(Σn)O(|\Sigma|n),足以通过本题。考虑如何更进一步优化。

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

    1

    每次修改的值只有 ai,sia_{i,s_i},我们便可以用 toito_i 表示该链的链头,每次修改 atoi,sia_{to_i,s_i} 即可。

    时间复杂度: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;
    }
    
    • 1

    信息

    ID
    9311
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者