poj3415 Common Substrings

链接

http://poj.org/problem?id=3415

题解

也不知是我码力下降了还是怎么的,这题竟然用了2.5h才搞定
先求一下后缀数组以及 height h e i g h t
那么最暴力的方法就先按照 K K 分组,每一个组内对每个属于B串的后缀,向上方查找所以属于 A A 的后缀,一路统计LCP
这样 O(n2) O ( n 2 ) 是不行的
对于每一个属于 B B 的后缀,我们面临的问题都是相似的,考虑能否进行优化
显然两个属于B的后缀中间的一些 height h e i g h t 会影响到上面某些属于 A A 的后缀的贡献,如果我们把每个属于A的后缀的贡献记录下来,每次加入一个后缀的时候,看下影响到了那些后缀的贡献,取个 min m i n 就好了
这样就想到了单调数据结构,如果我按照单调增的顺序存储这些贡献值,那么每次只需要修改结尾的连续几个
但是这样依然是不行的,修改的次数可能会很多,观察到修改之后这些贡献值都是相等的,那么我们就把修改后的元素合并一下,记录(出现次数,贡献值)这样的二元组,搞一搞就 OK O K
这个东西好像就是单调栈?
height h e i g h t 的值域的角度考虑,这个算法确实是 O(n) O ( n )
但是我只会 O(nlogn) O ( n l o g n ) 的后缀数组,所以最后复杂度是 O(nlogn) O ( n l o g n )

代码

//后缀数组
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <cmath>
#define maxn 200010
#define cl(x) memset(x,0,sizeof(x))
#define inf (1ll<<60)
#define ll long long
using namespace std;
struct con{ll cnt, contribution;}stack[maxn];
ll N, sa[maxn], rank[maxn], height[maxn], wa[maxn], wb[maxn], ws[maxn], wv[maxn], r[maxn], K, top, belong[maxn];
char A[maxn], B[maxn];
bool cmp(ll *r, ll a, ll b, ll l){return r[a]==r[b] and r[a+l]==r[b+l];}
void build_sa(ll *r, ll n, ll m)
{
    n++; 
    ll i, j, k=0, p, *x=wa, *y=wb, *t;
    for(i=0;i<m;i++)ws[i]=0;
    for(i=0;i<n;i++)ws[x[i]=r[i]]++;
    for(i=1;i<m;i++)ws[i]+=ws[i-1];
    for(i=n-1;i>=0;i--)sa[--ws[x[i]]]=i;
    for(p=j=1;p<n;j<<=1,m=p)
    {
        for(p=0,i=n-j;i<n;i++)y[p++]=i;
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
        for(i=0;i<n;i++)wv[i]=x[y[i]];
        for(i=0;i<m;i++)ws[i]=0;
        for(i=0;i<n;i++)ws[wv[i]]++;
        for(i=1;i<m;i++)ws[i]+=ws[i-1];
        for(i=n-1;i>=0;i--)sa[--ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,i=1,x[sa[0]]=0;i<n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    for(i=0;i<n;i++)rank[sa[i]]=i;
    for(i=0;i<n-1;height[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
}
void init()
{
    ll i, j, la, lb;
    cl(r), cl(sa), cl(rank), cl(height), cl(wa), cl(wb);
    scanf("%s%s",A,B);
    la=strlen(A), lb=strlen(B);
    for(i=0;i<la;i++)r[i]=A[i], belong[i]=1;
    for(i=0;i<lb;i++)r[la+1+i]=B[i], belong[la+1+i]=2;
    r[la]='|';
    N=la+1+lb;
    build_sa(r,N,300);
}
void calc()
{
    ll ans=0, i, j, pre=0, cnt, now;
    for(i=0;i<=N;i++)
        if(height[i+1]<K)
        {
            top=0, now=0;
            for(j=pre;j<=i;j++)
            {
                cnt=0;
                while(top and height[j]-K+1<stack[top].contribution)now-=stack[top].cnt*stack[top].contribution, cnt+=stack[top].cnt, top--;
                if(cnt)stack[++top]=(con){cnt,height[j]-K+1}, now+=cnt*(height[j]-K+1);
                if(belong[sa[j]]==1)stack[++top]=(con){1,inf}, now+=inf;
                else ans+=now;
            }
            pre=i+1;
        }
    pre=0;
    for(i=0;i<=N;i++)
        if(height[i+1]<K)
        {
            top=0, now=0;
            for(j=pre;j<=i;j++)
            {
                cnt=0;
                while(top and height[j]-K+1<stack[top].contribution)now-=stack[top].cnt*stack[top].contribution, cnt+=stack[top].cnt, top--;
                if(cnt)stack[++top]=(con){cnt,height[j]-K+1}, now+=cnt*(height[j]-K+1);
                if(belong[sa[j]]==2)stack[++top]=(con){1,inf}, now+=inf;
                else ans+=now;
            }
            pre=i+1;
        }
    printf("%lld\n",ans);
}
int main()
{
    while(scanf("%lld",&K),K)init(), calc();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值