[noip测试]拯救紫萱学姐(kmp+树形dp)

245 篇文章 0 订阅
12 篇文章 0 订阅

时间限制:1 s 内存限制:256 MB

题目描述

其实在开考前半个小时题面并不是这样的。
由于明天要考试,同学们要把抽屉里的书都搬空,书很多而且办了走读不能回寝室的学长一眼就看到了回班撩他的学姐,于是就把学姐当学长用♂了:“帮我把这摞书搬走OvO”。
学姐筋疲力尽地抱着沉重的一摞书回到了机房,出于无聊她翻开了学长的字典。
学长的字典由一个字符串组成。对于两个字符串a和b,如果a既是b的前缀也是b的后缀,那么称a和b“相似”,空字符串和任何字符串相似。一个字符串可以通过编辑变换成一个比它短而且与它相似的字符串,付出的代价为这两个字符串的长度之差的平方。两个字符串通过编辑而变为同一个字符串所花费的最小代价被称为最短编辑距离。
给定学长的字典,现在学姐想知道这个字符串的每一对前缀的最短编辑距离中的最大值是多少。请你帮助劳累的紫萱学姐解决这个问题。
【输入格式】
一个字符串,仅包含小写英文字母。
【输出格式】
这个字符串每一对前缀中最长的最短编辑距离。
【样例输入】
abcab
【样例输出】
23
【提示】
最短编辑距离最长的一对前缀是abca和abcab,最短变换过程如下(箭头中的数字为过程的代价)
“abca”-9->“a”-1->(空字符串)
“abcab”-9->“ab”-4->“”
对于40%的数据,n≤500。
对于70%的数据,n≤5000。
对于100%的数据,n≤1000000,n为字符串长度。

题解

比较好的一道思路题。
首先考虑暴力怎么做。由于是计算平方值,对于两个固定的串,肯定是把每一步分得越小越好。也就是说,每一个串都和它的失配有关。
考虑以i结尾和以j结尾的前缀,每次都暴力地将这两个点蹦到离当前点比较近的失配,然后从那个失配的答案转移。实际上是一个暴力dp,时间复杂度 O(n22logn)
考虑怎么将这个算法优化。可以发现,每一个点有可能向其后面的点转移,但是它只会由它的失配转移过来。这样实际上就是构成了一个树结构,树的边权就是点数的平方。这样的话,由于是求两个不同的前缀的答案,其实就是求树上不同的两个点距离的最大值,即为树的直径。时间复杂度 O(n2)

考试的时候有一些奇怪的思路,比如连边之后跑最长路。但是最长路是多源的发现并不能搞定。其实这个树的模型还是比较显然的。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 1000005
#define LL long long

int n;
char s[N];
int T[N];
int tot,point[N],nxt[N*2],v[N*2];LL c[N*2];
LL f[N],g[N],ans;

void calc()
{
    T[0]=-1;
    int j;
    for (int i=0;i<n;++i)
    {
        j=T[i];
        while (j!=-1&&s[j]!=s[i])
            j=T[j];
        T[i+1]=++j;
    }
}
void add(int x,int y,LL z)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
}
void treedp(int x,int fa)
{
    for (int i=point[x];i;i=nxt[i])
        if (v[i]!=fa)
        {
            treedp(v[i],x);
            if (f[v[i]]+c[i]>f[x])
            {
                g[x]=f[x];
                f[x]=f[v[i]]+c[i];
            }
            else g[x]=max(g[x],f[v[i]]+c[i]);
        }
    ans=max(ans,f[x]+g[x]);
}

int main()
{
    freopen("savemzx.in","r",stdin);
    freopen("savemzx.out","w",stdout);
    gets(s);n=strlen(s);
    calc();
    for (int i=1;i<=n;++i) add(T[i],i,((LL)i-(LL)T[i])*((LL)i-(LL)T[i]));
    treedp(0,-1);
    printf("%I64d\n",ans);
}

总结

①分析能力和建模能力还是有点弱啊。以后一定要多抽象,多联系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值