BZOJ3238: [Ahoi2013]差异 (后缀自动机)

Description

Input

一行,一个字符串S

Output

 

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

Sample Input

cacao

Sample Output


54

HINT

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

YY了后缀自动机的解法:

首先题意就是让你求sigma(LCP(i,j)|i<j)

将字符串反过来,考虑两个后缀对答案的贡献,其实就是节点x和y的lca节点包含的最长子串长度

那么将SAM构出来,考虑当LCA为节点z时,有多少满足条件的(x,y),这个枚举z的相邻子节点,dp一下即可

code:O(n) 2104ms

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=1000010;
int n,to[maxn][26],fa[maxn],l[maxn],f[maxn],x[maxn],w[maxn],od[maxn],cnt=1,last=1;
void extend(int c)
{
    int p,q,np,nq;
    p=last;last=np=++cnt;l[np]=l[p]+1;f[np]=w[np]=1;
    for(;!to[p][c];p=fa[p]) to[p][c]=np;
    if(!p) fa[np]=1;
    else
    {
        q=to[p][c];
        if(l[p]+1==l[q]) fa[np]=q;
        else
        {
            nq=++cnt;l[nq]=l[p]+1;
            memcpy(to[nq],to[q],sizeof(to[q]));
            fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            for(;to[p][c]==q;p=fa[p]) to[p][c]=nq;
        }
    }
}
LL solve()
{
    LL ans=0;
    for(int i=1;i<=cnt;i++) x[l[i]]++;
    for(int i=1;i<=n;i++) x[i]+=x[i-1];
    for(int i=1;i<=cnt;i++) od[x[l[i]]--]=i;
    for(int i=cnt;i;i--) f[fa[od[i]]]+=f[od[i]];
    for(int i=1;i<=cnt;i++)
    {
        ans+=(LL)w[fa[i]]*f[i]*l[fa[i]];
        w[fa[i]]+=f[i];
    }
    return ans;
}
char s[maxn];
int main()
{
    scanf("%s",s);
    n=strlen(s);
    for(int i=n-1;i>=0;i--) extend(s[i]-'a');
    LL ans=0;
    for(int i=1;i<=n;i++) ans+=(LL)i*(n-1);
    printf("%lld\n",ans-2*solve());
    return 0;
}
View Code

另外转一下hzwer的SA解法:

--------------------------------------------------------------------------------------------------------------------------------------------蒟蒻与神犇的分界线--------------------------------------------

显然后缀数组不是正确姿势。。。

不过还是说说后缀数组的做法吧,bzoj总时限20s是能过的

SA+rmq求lcp应该烂大街了,这题还不用rmq。。。

首先求出h数组

考虑h[i]在哪些区间内会成为最小值,这个用两次单调栈很容易就能解决

还要处理一下由于h[i]可能相同造成的重复计数问题,具体看代码

code O(nlogn) 13592ms

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 500005
#define inf 1000000000
#define pa pair<int,int>
#define ll long long 
using namespace std;
ll ans;
int n,k,p,q=1,top;
int v[N],a[N],h[N],sa[2][N],rk[2][N];
int st[N],l[N],r[N];
char ch[N];
void mul(int *sa,int *rk,int *SA,int *RK)
{
    for(int i=1;i<=n;i++)v[rk[sa[i]]]=i;
    for(int i=n;i;i--)
        if(sa[i]>k)
            SA[v[rk[sa[i]-k]]--]=sa[i]-k;
    for(int i=n-k+1;i<=n;i++)SA[v[rk[i]]--]=i;
    for(int i=1;i<=n;i++)
        RK[SA[i]]=RK[SA[i-1]]+(rk[SA[i-1]]!=rk[SA[i]]||rk[SA[i-1]+k]!=rk[SA[i]+k]);
}
void presa()
{
    for(int i=1;i<=n;i++)v[a[i]]++;
    for(int i=1;i<=30;i++)v[i]+=v[i-1];
    for(int i=1;i<=n;i++)sa[p][v[a[i]]--]=i;
    for(int i=1;i<=n;i++)
        rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(a[sa[p][i-1]]!=a[sa[p][i]]);
    for(k=1;k<n;k<<=1,swap(p,q))
        mul(sa[p],rk[p],sa[q],rk[q]);
    for(int k=0,i=1;i<=n;i++)
    {
        int j=sa[p][rk[p][i]-1];
        while(ch[j+k]==ch[i+k])k++;
        h[rk[p][i]]=k;if(k>0)k--;           
    }
}
void solve()
{
    for(int i=1;i<=n;i++)ans+=(ll)i*(n-1);
    h[0]=-inf;
    for(int i=1;i<=n;i++)
    {
        while(h[i]<=h[st[top]])top--;
        if(st[top]==0)l[i]=1;
        else l[i]=st[top]+1;
        st[++top]=i;
    }
    h[n+1]=-inf;top=0;st[0]=n+1;
    for(int i=n;i;i--)
    {
        while(h[i]<h[st[top]])top--;
        if(st[top]==n+1)r[i]=n;
        else r[i]=st[top]-1;
        st[++top]=i;
    }
    for(int i=1;i<=n;i++)
        ans-=2LL*(i-l[i]+1)*(r[i]-i+1)*h[i];
}
int main()
{
    scanf("%s",ch+1);
    n=strlen(ch+1);
    for(int i=1;i<=n;i++)a[i]=ch[i]-'a'+1;
    presa();
    solve();
    printf("%lld",ans);
    return 0;
}
View Code

 

 

posted @ 2015-06-08 15:54  wzj_is_a_juruo  阅读(1055)  评论(0编辑  收藏  举报