[AHOI2013]差异 & [HAOI2016]找相同字符
链接
把这两道题放在一起是要讨论这样一个问题
对于一个已知 \(ht\) 的 \(S\),\(O(n)\) 求 \(\sum\limits_{1\le i<j\le n} lcp(i,j)\)
首先我们可以把上式写成 \(\sum\limits_{1\le i\le j}\sum\limits_{1\le j<i}lcp(i,j)\),也就是对于排序后的后缀的一个位置,我们只考虑排名在它之前的后缀与它的lcp。那么我们依次地考虑每个后缀,发现我们实际需要做的是维护一个单调升序列,于是我们只需要用单调栈维护一下 \(ht\) 值和位置就好了。
int sta[N],pos[N],sum[N],now,ans;
inline int solve(){//\sum_{1\le i<j\le n}lcp(i,j)
pos[0]=1,ans=0,now=0;
for(int i=2;i<=n;i++){
while(now&&ht[i]<=sta[now])now--;
sta[++now]=ht[i],pos[now]=i,
sum[now]=sum[now-1]+ht[i]*(pos[now]-pos[now-1]),
ans+=sum[now];
}
return ans;
}
好了回到这两道题。
分析
P4248 [AHOI2013]差异
我们把题目的式子稍微化化,就是
\((n-1)\times\frac{n\times (n+1)}2-2\sum\limits_{1\le i<j\le n} lcp(i,j)\)
直接上。
P3181 [HAOI2016]找相同字符
我们发现发现这题要求的仍然是上面类似的东西,只是子串的来源被分到了两个字符串,这种情况,我们经常用一个特殊字符将两式连接起来一起处理。这样做了后我们发现会算多一部分,这是从某一个串选了两个子串的答案,所以我们只用把两个子串分别处理的结果减掉就好了。
代码
P4248 [AHOI2013]差异
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
int p=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-'0';c=getchar();}
return p*f;
}
const int N=5e5+5;
int n,m,rk[N<<1],sa[N],ht[N];
struct llmmkk{
int fi,se,ref;
}st[N<<1],tmp[N<<1];
int cnt[N];
inline void jsort(){
for(int i=0;i<=n;i++)cnt[i]=0;
for(int i=1;i<=n;i++)cnt[st[i].se]++;
for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>0;i--)tmp[cnt[st[i].se]]=st[i],cnt[st[i].se]--;
for(int i=0;i<=n;i++)cnt[i]=0;
for(int i=1;i<=n;i++)cnt[tmp[i].fi]++;
for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>0;i--)st[cnt[tmp[i].fi]]=tmp[i],cnt[tmp[i].fi]--;
}
int ston[257];
inline void SA(string S){
n=S.length();
for(int i=0;i<n;i++)ston[signed(S[i])]+=(!ston[signed(S[i])]);
for(int i=1;i<=256;i++)ston[i]+=ston[i-1];
for(int i=1;i<=n;i++)rk[i]=ston[signed(S[i-1])];
for(int k=1,t;k<=(n<<1);k<<=1){
for(int i=1;i<=n;i++)
st[i].fi=rk[i],st[i].se=rk[i+k],st[i].ref=i;
jsort();t=0;
for(int i=1;i<=n;i++){
if((st[i].fi^st[i-1].fi)||(st[i].se^st[i-1].se))t++;
rk[st[i].ref]=t;
}if(t==n)break;
}
for(int i=1;i<=n;i++)sa[rk[i]]=i;
for(int i=1;i<=n;i++){
ht[rk[i]]=max(ht[rk[i-1]]-1,0ll);
while(S[sa[rk[i]-1]+ht[rk[i]]-1]==S[i+ht[rk[i]]-1])
ht[rk[i]]++;
}
}
string S;
int sta[N],pos[N],sum[N],now,ans;
inline int solve(){
pos[0]=1,ans=0,now=0;
for(int i=2;i<=n;i++){
while(now&&ht[i]<=sta[now])now--;
sta[++now]=ht[i],pos[now]=i,
sum[now]=sum[now-1]+ht[i]*(pos[now]-pos[now-1]),
ans+=sum[now];
}
return ans;
}
signed main(){
cin>>S;SA(S);
ans=(n+1)*n/2*(n-1)-2*solve();
cout<<ans;
return 0;
}
P3181 [HAOI2016]找相同字符
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define in read()
inline int read(){
int p=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-'0';c=getchar();}
return p*f;
}
const int N=4e5+5;
int n,rk[N<<1],sa[N],ht[N];
struct llmmkk{
int fi,se,ref;
}st[N<<1],tmp[N<<1];
int cnt[N];
inline void jsort(){
for(int i=0;i<=n;i++)cnt[i]=0;
for(int i=1;i<=n;i++)cnt[st[i].se]++;
for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>0;i--)tmp[cnt[st[i].se]]=st[i],cnt[st[i].se]--;
for(int i=0;i<=n;i++)cnt[i]=0;
for(int i=1;i<=n;i++)cnt[tmp[i].fi]++;
for(int i=1;i<=n;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>0;i--)st[cnt[tmp[i].fi]]=tmp[i],cnt[tmp[i].fi]--;
}
int ston[257];
inline void SA(string S){
n=S.length();memset(ston,0,sizeof(ston));
for(int i=0;i<n;i++)ston[signed(S[i])]+=(!ston[signed(S[i])]);
for(int i=1;i<=256;i++)ston[i]+=ston[i-1];
for(int i=1;i<=n;i++)rk[i]=ston[signed(S[i-1])];
for(int k=1,t;k<=(n<<1);k<<=1){
for(int i=1;i<=n;i++)
st[i].fi=rk[i],st[i].se=rk[i+k],st[i].ref=i;
jsort();t=0;
for(int i=1;i<=n;i++){
if((st[i].fi^st[i-1].fi)||(st[i].se^st[i-1].se))t++;
rk[st[i].ref]=t;
}if(t==n)break;
}
for(int i=1;i<=n;i++)sa[rk[i]]=i;
for(int i=1;i<=n;i++){
ht[rk[i]]=max(ht[rk[i-1]]-1,0ll);
while(S[sa[rk[i]-1]+ht[rk[i]]-1]==S[i+ht[rk[i]]-1])
ht[rk[i]]++;
}
}
string S,T;
int sta[N],pos[N],sum[N],now,ans;
inline int solve(){//\sum_{1\le i<j\le n}lcp(i,j)
pos[0]=1,ans=0,now=0;
for(int i=2;i<=n;i++){
while(now&&ht[i]<=sta[now])now--;
sta[++now]=ht[i],pos[now]=i,
sum[now]=sum[now-1]+ht[i]*(pos[now]-pos[now-1]),
ans+=sum[now];
}
return ans;
}
int tans=0;
signed main(){
cin>>S>>T;
SA(S),tans-=solve();
SA(T),tans-=solve();
SA(S+'#'+T),tans+=solve();
cout<<tans;
return 0;
}