[HDU 5659]CA Loves Substring

一、题目

点此看题
题目描述
多组数据,给你一个字符串,求从每一个字符断开(分成 s [ 1 , i ] s[1,i] s [ i + 1 , n ] s[i+1,n] )分成的子串中本质不同的字符串数量,输出需要压缩,详见英文版题目。
数据范围
1 n 200000 1\leq n\leq200000

二、解法

正难则反,我们考虑计算出本质不同的子串数量,然后计算出跨过断开处且没有在两边出现过的子串数量,相减即可。

f [ i ] f[i] 为断开 i i 处的满足上述条件的子串数量,考虑每一种子串的贡献,设它出现右端点最大时 m a ma ,最小是 m i mi ,如果有 m a l e n + 1 m i 1 ma-len+1\leq mi-1 ,那就说明该子串有贡献,把 f [ m a l e n + 1.... m i 1 ] f[ma-len+1....mi-1] 这个段的值加1。

子串数量太多怎么办,可以再次压缩,我们使用后缀自动机。一个集合中的子串是可以一次性处理的,假设我们已经得到了自动机上每个点的 m a , m i , l e n , m l ma,mi,len,ml l e n len 是最大长度, m l ml 是最小长度),对于 f [ m a l e n + 1 , m a m l + 1 ] f[ma-len+1,ma-ml+1] 这段区间呈等差数列增加,差分再差分。而对于 f [ m a m l + 2 , m i 1 ] f[ma-ml+2,mi-1] 这段区间每个都加上 l e n m l + 1 len-ml+1 ,直接差分即可。

时间复杂度 O ( n ) O(n) ,详见代码。

#include <cstdio>
#include <cstring>
#include <iostream>
#define int long long
using namespace std;
const int M = 400005;
const int MOD = 1e9+7;
int read()
{
 int x=0,flag=1;char c;
 while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
 while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
 return x*flag;
}
int n,sum,ans,cnt,last,tot,f[M],l[M],r[M],dp[M],g[M],p[M];
char s[M];
struct node
{
    int len,fa,ch[26];
    node() {memset(ch,0,sizeof ch);len=fa=0;}
    void clear() {memset(ch,0,sizeof ch);len=fa=0;}
}a[M];
struct edge
{
    int v,next;
    edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
void add(int c)
{
    int p=last,np=last=++cnt;
    a[np].clear();
    l[np]=r[np]=a[np].len=a[p].len+1;
    for(;p && !a[p].ch[c];p=a[p].fa) a[p].ch[c]=np;
    if(!p) a[np].fa=1;
    else
    {
        int q=a[p].ch[c];
        if(a[q].len==a[p].len+1) a[np].fa=q;
        else
        {
            int nq=++cnt;
            a[nq]=a[q];a[nq].len=a[p].len+1;
            a[q].fa=a[np].fa=nq;
            for(;p && a[p].ch[c]==q;p=a[p].fa) a[p].ch[c]=nq;
        }
    }
}
void init()
{
    a[1].clear();
    cnt=last=1;tot=ans=sum=0;
    memset(f,0,sizeof f);
    memset(dp,0,sizeof dp);
    memset(g,0,sizeof g);
    n=read();scanf("%s",s);
    for(int i=0;i<n;i++)
        add(s[i]-'0');
}
void link(int u,int v)
{
    e[++tot]=edge(v,f[u]),f[u]=tot;
    e[++tot]=edge(u,f[v]),f[v]=tot;
}
void dfs(int u,int p)
{
    sum+=a[u].len-a[p].len;
    for(int i=f[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==p) continue;
        dfs(v,u);
        l[u]=min(l[u],l[v]);
        r[u]=max(r[u],r[v]);
    }
    //以下是关键代码,应该看得懂qwq
    if(r[u]-a[u].len+1>l[u]-1) return ;
    int ml=a[p].len+1;
    if(r[u]-ml+2<=l[u]-1)
    {
        dp[r[u]-ml+2]+=a[u].len-ml+1;
        dp[l[u]]-=a[u].len-ml+1;
    }
    int rr=min(r[u]-ml+1,l[u]-1);
    g[r[u]-a[u].len+1]++;
    g[rr+1]--;
    dp[rr+1]-=rr-(r[u]-a[u].len+1)+1;
}
signed main()
{
    p[0]=1;
    for(int i=1;i<=200000;i++)
        p[i]=p[i-1]*100013%MOD;
    int T=read();
    while(T--)
    {
        init();
        for(int i=2;i<=cnt;i++)
            link(i,a[i].fa);
        dfs(1,0);
        for(int i=1;i<n;i++)
        {
            g[i]+=g[i-1];
            dp[i]+=dp[i-1];
            dp[i]+=g[i];
            ans=(ans+(sum-dp[i])%MOD*p[n-i-1]%MOD)%MOD;
        }
        printf("%lld\n",(ans%MOD+MOD)%MOD);
    }
}
发布了217 篇原创文章 · 获赞 12 · 访问量 5141

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/104101201