一、题目
点此看题
题目描述
多组数据,给你一个字符串,求从每一个字符断开(分成
和
)分成的子串中本质不同的字符串数量,输出需要压缩,详见英文版题目。
数据范围
二、解法
正难则反,我们考虑计算出本质不同的子串数量,然后计算出跨过断开处且没有在两边出现过的子串数量,相减即可。
设 为断开 处的满足上述条件的子串数量,考虑每一种子串的贡献,设它出现右端点最大时 ,最小是 ,如果有 ,那就说明该子串有贡献,把 这个段的值加1。
子串数量太多怎么办,可以再次压缩,我们使用后缀自动机。一个集合中的子串是可以一次性处理的,假设我们已经得到了自动机上每个点的 ( 是最大长度, 是最小长度),对于 这段区间呈等差数列增加,差分再差分。而对于 这段区间每个都加上 ,直接差分即可。
时间复杂度 ,详见代码。
#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);
}
}