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 MB
Submit: 4447  Solved: 2057
[Submit][Status][Discuss]

Description

Input

一行,一个字符串S

Output

 

一行,一个整数,表示所求值

Sample Input

cacao

Sample Output


54

HINT



2<=N<=500000,S由小写英文字母组成

posted @ 2018-08-05 16:44  wang9897  阅读(89)  评论(0编辑  收藏  举报