博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
COGS 2510. 拯救紫萱学姐
阅读量:5885 次
发布时间:2019-06-19

本文共 2174 字,大约阅读时间需要 7 分钟。

【题目描述】

 

其实在开考前半个小时题面并不是这样的。

由于明天要考试,同学们要把抽屉里的书都搬空,书很多而且办了走读不能回寝室的学长一眼就看到了回班撩他的学姐,于是就把学姐当学长用♂了:“帮我把这摞书搬走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为字符串长度。

 

 

题解:

  不得不承认,这是一道非常好的题目,首先,我们可以考虑用哈希预处理出所以可能的转移,但这个题目可以贪心的思考,对于每个可能的相似前缀,只要考虑向最大的相似前缀转移就可以了。理由如下:

  1.对于一个前缀转移到一个更小的相似前缀,肯定是每次转移长度之差越小越好,因为a^2+b^2<x^2(x=a+b),所以我们每次转移的最大可转移的相似前缀上一定花费最小。

  2.对于前缀x的多种转移中次大的转移一定可以由最大的转移 转移过来,如一个len=7的前缀,可以转移到长度为5,长度为3,长度为0,这三种前缀上,那么5必定可以转移到3,3必定可以转移到0.

  所以我们只要对考虑最大的前缀就可以了(用类似于next数组的求发求)。

  如果我们把每个前缀看成一个节点(编号为前缀长度),然后向可转移的节点连边,边权为长度差,那么显然答案就变成了求任意两点到另一点的最短路之和,不难发现,连出来的边是一棵树,因为每个节点只会向前面的节点连边,并且对于每条链都会最后连到0节点。然后我们发现不用枚举所有的前缀作为两个前缀变成的相似前缀,因为就是我们所选两个节点的lca,这个十分显然因为边权是>0的,那么我们把边变成双向,就转化成了树上求直径问题了。

 

代码:

#include 
#include
#include
#include
#include
#include
#define MAXN 1000100#define ll long longusing namespace std;struct edge{ int first; int next; int to; ll quan;}a[MAXN*3];int f[MAXN],roof=0,n;ll dis[MAXN],dis2[MAXN],num=0;char s[MAXN];void pre(){ f[1]=0; for(int i=2;i<=n;i++){ int j=f[i-1]; while(j&&s[i]!=s[j+1]) j=f[j]; if(s[i]==s[j+1]) f[i]=j+1; else f[i]=j; }}void addedge(int from,int to,ll quan){ a[++num].to=to; a[num].quan=quan; a[num].next=a[from].first; a[from].first=num;}void dfs(int now,int fa,ll *dis){ for(int i=a[now].first;i;i=a[i].next){ ll to=a[i].to,quan=a[i].quan; if(to==fa) continue; dis[to]=dis[now]+quan; dfs(to,now,dis); }}int main(){ scanf("%s",s+1); n=strlen(s+1); pre(); for(int i=1;i<=n;i++){ addedge(i,f[i],(long long)(i-f[i])*(i-f[i])); addedge(f[i],i,(long long)(i-f[i])*(i-f[i])); } dfs(0,0,dis); for(int i=1;i<=n;i++) if(dis[roof]

 

转载于:https://www.cnblogs.com/renjianshige/p/7429944.html

你可能感兴趣的文章
软件工程敏捷开发04
查看>>
Practise Site Home Sample Page Codes de carte cadeau Amazon | Codes Promo Amazon
查看>>
linux c下输入密码不回显
查看>>
【C语言】练习1-23
查看>>
在Linux命令行下发送html格式的邮件
查看>>
说说PHP中foreach引用的一个坑
查看>>
基于express框架的应用程序骨架生成器介绍
查看>>
Spring学习11-Spring使用proxool连接池 管理数据源
查看>>
2016第6周五
查看>>
ASP.NET 免费开源控件
查看>>
面向对象葵花宝典阅读思维导图(二)
查看>>
volatile关键字与线程间通信
查看>>
优秀大数据GitHub项目一览
查看>>
TCP/IP详解学习笔记(8)-DNS域名系统
查看>>
通过维基API实现维基百科查询功能
查看>>
bootstrap 2
查看>>
Annotation研究的一些学习资料
查看>>
webpack资料
查看>>
DotNet加密方式解析--散列加密
查看>>
OpenSSL使用2(SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12概念说明)(转)
查看>>