BZOJ3238: [Ahoi2013]差异
题解: 难点是如何求任意两个后缀的lcp 我们考虑到任意两个位置的后缀LCP取决于两者在SA中的最小值 因此我们有两种方法解决 首先对于sa数组按照h数组分治 统计每个位置的贡献 其次我们也可以单调栈找到每个位置前面第一个比他小的 和 后面第一个比他小的然后统计价值即可
分治版本:
/**************************************************************
Problem: 3238
User: wang9897
Language: C++
Result: Accepted
Time:2968 ms
Memory:19360 kb
****************************************************************/
#include <bits/stdc++.h>
#define N 500005
#define INF 100000007
#define ll long long
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
char s[N];int sa[N],txt[N],rank1[N],t1[N],t2[N],rank2[N],td[N];
bool cmp(int f[],int t,int w,int k){
return f[t]==f[w]&&f[t+k]==f[w+k];
}
void Sa(char str[]){
int len=strlen(str);
int m=127;
int *rank1=t2;int *td=t1;
for(int i=0;i<m;i++) txt[i]=0;
for(int i=0;i<len;i++){
rank1[i]=str[i];txt[str[i]]++;
}
for(int i=1;i<m;i++) txt[i]+=txt[i-1];
for(int i=len-1;i>=0;i--) sa[--txt[str[i]]]=i;
for(int k=1;k<=len;k*=2){
int p=0;
for(int i=len-k;i<len;i++) td[p++]=i;
for(int i=0;i<len;i++){
if(sa[i]>=k) td[p++]=sa[i]-k;
}
for(int i=0;i<m;i++) txt[i]=0;
for(int i=0;i<len;i++) txt[rank1[i]]++;
for(int i=1;i<m;i++) txt[i]+=txt[i-1];
for(int i=len-1;i>=0;i--) sa[--txt[rank1[td[i]]]]=td[i];
swap(td,rank1);
rank1[sa[0]]=0;
p=1;
for(int i=1;i<len;i++) rank1[sa[i]]=cmp(td,sa[i],sa[i-1],k)?p-1:p++;
if(p==len) return ;
m=p;
}
}
int hh[N],height[N];
void h(char str[]){
int len=strlen(str);
for(int i=0;i<len;i++) rank2[sa[i]]=i;
memset(hh,0,sizeof(hh));
for(int i=0;i<len;i++){
if(rank2[i]==0) continue;
int t=sa[rank2[i]-1];int w=i;int k;
if(i==0||hh[i-1]<=1) k=0;
else{
k=hh[i-1]-1;t+=k;w+=k;
}
while(t<len&&w<len){
if(str[t]==str[w]) k++;
else break;
t++;w++;
}
hh[i]=k;height[rank2[i]]=k;
}
return ;
}
ll ans,ans1,ans2;
void fenzhi(int l,int r){
if(l>r) return ;
// cout<<l<<" "<<r<<endl;
ll t=INF;int tt;
if(l==r){
ans+=height[l];return ;
}
for(int i=l;i<=r;i++){
if(height[i]<t){
t=height[i];tt=i;
}
}
ans+=1ll*(r-tt+1)*(tt-l+1)*t;
fenzhi(l,tt-1);
fenzhi(tt+1,r);
}
int main(){
ios::sync_with_stdio(false);
scanf(" %s",&s);int len=strlen(s);
s[len]='$';
Sa(s);h(s);
ans=0;ans1=0;ans2=0;
fenzhi(1,len);
ans*=2;
for(int i=len;i>=2;i--) ans1+=1ll*i*(i-1);
for(int i=len-1;i>=1;i--) ans2+=1ll*(i+1)*i/2;
printf("%lld\n",ans1+ans2-ans);
return 0;
}
单调栈:
/**************************************************************
Problem: 3238
User: wang9897
Language: C++
Result: Accepted
Time:5888 ms
Memory:73216 kb
****************************************************************/
#include <bits/stdc++.h>
#define ll long long
const int MAXN=5e5+10;
using namespace std;
int txt[MAXN],t1[MAXN],t2[MAXN],rank1[MAXN],rank2[MAXN],td[MAXN],sa[MAXN];
bool cmp(int f[],int t,int w,int k){return f[t]==f[w]&&f[t+k]==f[k+w];}
void Sa(char str[]){
int len=strlen(str);int m=250;
int *td=t1;int *rank1=t2;
for(int i=0;i<m;i++)txt[i]=0;
for(int i=0;i<len;i++)rank1[i]=str[i],txt[str[i]]++;
for(int i=1;i<m;i++)txt[i]+=txt[i-1];
for(int i=len-1;i>=0;i--)sa[--txt[str[i]]]=i;
for(int k=1;k<=len;k*=2){
int p=0;
for(int i=len-k;i<len;i++)td[p++]=i;
for(int i=0;i<len;i++)if(sa[i]>=k)td[p++]=sa[i]-k;
for(int i=0;i<m;i++)txt[i]=0;
for(int i=0;i<len;i++)txt[rank1[i]]++;
for(int i=1;i<m;i++)txt[i]+=txt[i-1];
for(int i=len-1;i>=0;i--)sa[--txt[rank1[td[i]]]]=td[i];
swap(rank1,td);rank1[sa[0]]=0;p=1;
for(int i=1;i<len;i++)rank1[sa[i]]=cmp(td,sa[i],sa[i-1],k)?p-1:p++;
if(p==len)return ;
m=p;
}
}
int h[MAXN],H[MAXN];
void hh(char str[]){
int len=strlen(str);
memset(h,0,sizeof(h));memset(H,0,sizeof(H));
for(int i=0;i<len;i++)rank2[sa[i]]=i;
for(int i=0;i<len;i++){
if(!rank2[i])continue;
int t=sa[rank2[i]-1];int w=i;int k=0;
if(!i||H[i-1]<=1)k=0;
else k=H[i-1]-1,t+=k,w+=k;
while(t<len&&w<len){
if(str[t]==str[w])k++;
else break;
t++;w++;
}
H[i]=k;h[rank2[i]]=k;
}
}
int dp[MAXN][21],ma[MAXN];
void St(char str[]){
int len=strlen(str);
ma[0]=-1;
for(int i=1;i<MAXN;i++)if((i&(i-1))==0)ma[i]=ma[i-1]+1;else ma[i]=ma[i-1];
for(int i=1;i<len;i++)dp[i][0]=h[i];
for(int j=1;(1<<(j-1))<=len;j++){
for(int i=1;i+(1<<j)<=len;i++){
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int rmq(int l,int r){
if(l>=r)return 0;
l++;
int k=ma[r-l+1];int k1=(1<<k);
return min(dp[l][k],dp[r-k1+1][k]);
}
char s[MAXN];
pair<int,int> st[MAXN];int tot;int rnum[MAXN],lnum[MAXN];
vector<int>vec;
int main(){
scanf("%s",s);int len=strlen(s);s[len++]='$';
Sa(s);hh(s);St(s);
// for(int i=1;i<len;i++)cout<<sa[i]<<" ";
// cout<<endl;
for(int i=2;i<len;i++)vec.push_back(rmq(i-1,i));
int sz=vec.size();
// for(int i=0;i<sz;i++)cout<<vec[i]<<" ";
// cout<<endl;
for(int i=0;i<sz;i++){
while(tot>0&&st[tot].first>vec[i]){tot--;}
if(!tot)lnum[i]=i+1;
else lnum[i]=i-st[tot].second;
// cout<<lnum[i]<<"::::";
st[++tot]=make_pair(vec[i],i);
}
// cout<<endl;
reverse(vec.begin(),vec.end());tot=0;
for(int i=0;i<sz;i++){
while(tot>0&&st[tot].first>=vec[i]){tot--;}
if(!tot)rnum[sz-i-1]=i+1;
else rnum[sz-i-1]=i-st[tot].second;
st[++tot]=make_pair(vec[i],i);
}
reverse(vec.begin(),vec.end());
int n=len-1;
ll ans=1ll*(n-1)*(n+1)*n/2;
for(int i=0;i<sz;i++)ans-=1ll*2*lnum[i]*rnum[i]*vec[i];
printf("%lld\n",ans);
}
3238: [Ahoi2013]差异
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4447 Solved: 2057
[Submit][Status][Discuss]
Description
.jpg)
Input
一行,一个字符串S
Output
一行,一个整数,表示所求值
Sample Input
cacao
Sample Output
54
HINT
2<=N<=500000,S由小写英文字母组成

浙公网安备 33010602011771号