test 拯救紫萱学姐 (kmp+树的直径)

69 篇文章 0 订阅
16 篇文章 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为字符串长度。



题解:kmp+树的直径

这道题要求的是最短编辑距离的最大值。失配就是最长的前缀等于后缀的长度,因为每次是长度差的平方,所以每次变换后的长度越长越好,那么必然是每次都变成他的失配,那么用那两个更新答案,并且保证到达他们两个恰好相等的点,并且不会再多向上走呢?我们从每次每次前缀向他的失配连边,然后就形成了一颗树,边长(i-t[i])^2。那其实就是求以其中某个点为终点的两条最长路,其实就是树的直径。这用就可以求解啦。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 2100003
#define LL long long
using namespace std;
char s[N];
int n,t[N],tot;
int point[N],v[N],next[N];
LL f[N],g[N],ans,c[N];
void add(int x,int y,LL z)
{
	tot++; next[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
	tot++; next[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
	//cout<<x<<" "<<y<<" "<<z<<endl;
}
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;
	 }
}
LL pow1(int x)
{
	return (LL)x*(LL)x;
}
void dfs(int x,int fa)
{
	f[x]=g[x]=0;
	for (int i=point[x];i;i=next[i])
	 if (v[i]!=fa) {
	 	 dfs(v[i],x);
	 	 LL t=f[v[i]]+c[i];
	 	 if (t>f[x]) g[x]=f[x],f[x]=t;
	 	 else if (t>g[x]) g[x]=t;
    }
    ans=max(ans,g[x]+f[x]);
}
int main()
{
	freopen("savemzx.in","r",stdin);
	freopen("savemzx.out","w",stdout);
	scanf("%s",s);
	n=strlen(s);
	calc();
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	LL maxn=0;
	for (int i=1;i<=n;i++) add(t[i],i,pow1(i-t[i]));
	//cout<<tot<<endl;
	dfs(0,-1);
	printf("%I64d\n",ans);
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值