HDU6583:Typewriter(dp+后缀自动机)
题意:
给出\(p,q\),现在要你生成一个字符串\(s\)。
你可以进行两种操作:一种是花费\(p\)的代价随意在后面添加一个字符,另一种是花费\(q\)的代价可以随意赋值前面的一个子串。
现在问最小代价是多少。
思路:
考虑\(dp\),那么就有转移方程:\(dp[i]=min\{dp[i-1]+p,dp[j]+q\}\),即直接对两种操作取min即可。
注意到该\(dp\)方程有一个性质:其值肯定为单调不降的。因为如果有\(dp[i-1]>dp[i]\),那此时我们跟着复制过来肯定更好,不能复制就花\(p\)在后面增加,那值岂不是也变大了?
所以转移方程中的\(j\)应为最远的一个\(j\)。
之后思路如下:
- 考虑维护离当前\(i\)最远的一个\(j\),使得\(s[1,\cdots,j-1]\)的子串中含有\(s[j,\cdots,i]\)。
- 注意到\(j\)是单调不减的,考虑用后缀自动机来维护这样一个位置。
- 当向\(i+1\)转移时,若目前后缀自动机中无法成功向\(s[i+1]\)转移,那么将第\(j\)个字符加入后缀自动机并且\(j++\),直到成功转移。
- 注意我们始终要保证后缀自动机中的状态长度尽可能小,这样我们才能保证\(j\)尽可能远,详见\(withdraw\)操作。
挺好的一道题,需要透彻分析问题的性质。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4e5 + 5;
int p, q;
char s[N];
struct SAM{
struct node{
int ch[26];
int len, fa;
node(){memset(ch, 0, sizeof(ch)), len = 0;}
}dian[N];
int last, tot, now;
void init(int n) {
last = tot = now = 1;
for(int i = 1; i <= 2 * n; i++) {
for(int j = 0; j < 26; j++) dian[i].ch[j] = 0;
dian[i].len = 0;
}
}
void add(int c) {
int p = last;
int np = last = ++tot;
dian[np].len = dian[p].len + 1;
for(; p && !dian[p].ch[c]; p = dian[p].fa) dian[p].ch[c] = np;
if(!p) dian[np].fa = 1;
else {
int q = dian[p].ch[c];
if(dian[q].len == dian[p].len + 1) dian[np].fa = q;
else {
int nq = ++tot; dian[nq] = dian[q];
dian[nq].len = dian[p].len + 1;
dian[q].fa = dian[np].fa = nq;
for(; p && dian[p].ch[c] == q; p = dian[p].fa) dian[p].ch[c] = nq;
}
}
}
void withdraw(int lens) {
while(now && dian[dian[now].fa].len >= lens) now = dian[now].fa;
if(now == 0) now = 1;
}
void trans(int t, int lens) {
now = dian[now].ch[t];
withdraw(lens);
}
bool match(int t) {
return dian[now].ch[t];
}
}A;
ll dp[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0);
while(cin >> s + 1) {
int n = strlen(s + 1);
cin >> p >> q;
A.init(n);
int l = 2, r = 1;
A.add(s[1] - 'a'); dp[1] = p;
for(int i = 2; i <= n; i++) {
++r; int tmp = s[i] - 'a';
dp[i] = dp[i - 1] + p;
while((!A.match(tmp) || r - l + 1 > i / 2) && l <= r) {
A.add(s[l++] - 'a');
A.withdraw(r - l);
}
A.trans(tmp, r - l + 1);
dp[i] = min(dp[i], dp[l - 1] + q);
}
cout << dp[n] << '\n';
}
return 0;
}
HDU6583:Typewriter(dp+后缀自动机)的更多相关文章
- hdu多校第一场 1006 (hdu6583)Typewriter dp/后缀自动机
题意: 有个打字机,在当前字符串后新加一个字花费p,把当前字符串的一个连续子串拷贝到当前字符串的末尾花费q,给定一个字符串,求用打字机打出这个字符串的最小花费. 题解: 容易想到用dp 记dp[i]为 ...
- [2019杭电多校第一场][hdu6583]Typewriter(后缀自动机&&dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6583 大致题意是说可以花费p在字符串后添加一个任意字符,或者花费q在字符串后添加一个当前字符串的子串. ...
- BZOJ 2806: [Ctsc2012]Cheat(单调队列优化dp+后缀自动机)
传送门 解题思路 肯定先要建出来广义后缀自动机.刚开始以为是个二分+贪心,写了一下结果\(20\)分.说一下正解,首先显然\(L_0\)具有单调性,是可以二分的.考虑二分后怎样判合法,对于分割序列很容 ...
- BZOJ.4032.[HEOI2015]最短不公共子串(DP 后缀自动机)
题目链接 1.求A的最短子串,它不是B的子串. 子串是连续的,对B建SAM,枚举起点,在SAM上找到第一个无法匹配点即可.O(n)用SAM能做吗..开始想错了. 2.求A的最短子串,它不是B的子序列. ...
- [BZOJ4199][Noi2015]品酒大会 树形DP+后缀自动机
由于要找后缀的前缀,所以先用反串建立SAM. link边组成了后缀树. 两个子串的最长公共前缀是LCA的step 树形dp即可. #include<iostream> #include&l ...
- codeforces#1120C. Compress String(dp+后缀自动机)
题目链接: https://codeforces.com/contest/1120/problem/C 题意: 从前往后压缩一段字符串 有两种操作: 1.对于单个字符,压缩它花费$a$ 2.对于末尾一 ...
- HDU - 6583 Typewriter (后缀自动机+dp)
题目链接 题意:你要打印一段字符串,往尾部添加一个字符需要花费p元,复制一段字符到尾部需要花费q元,求打印完全部字符的最小花费. 一开始想的贪心,后来发现忘了考虑p<q的情况了,还纳闷怎么不对. ...
- bzoj 2806: [Ctsc2012]Cheat 后缀自动机DP
2806: [Ctsc2012]Cheat Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 583 Solved: 330[Submit][Statu ...
- [十二省联考2019]字符串问题——后缀自动机+parent树优化建图+拓扑序DP+倍增
题目链接: [十二省联考2019]字符串问题 首先考虑最暴力的做法就是对于每个$B$串存一下它是哪些$A$串的前缀,然后按每组支配关系连边,做一遍拓扑序DP即可. 但即使忽略判断前缀的时间,光是连边的 ...
随机推荐
- 【day05】css
一.盒模型(BoxModel) 1.width 宽度 2.height 高度 说明: 块元素和有宽高属性的标记(img,input) 能设置宽度和高度,而行元素不能设置宽高 3 ...
- 【K短路】牛慢跑
牛慢跑 据说是\(k\)短路模板,要用\(A^*\),然而我不会.我是用拓扑排序加堆优化广搜水过去的.第一道完全靠自己做出来的紫题,调了两个小时,交了两遍.果然我还是太菜了. 正解的话,可以看红太阳的 ...
- vue_02day练习
目录 vue_02day 作业 vue 框架 :成绩单的实现 v-for 与 v-if 联用: 3.添加筛选规则(深入) : 留言功能的页面实现: vue_02day 作业 1. 先有一下成绩单数据 ...
- 申请Github学生包(用学生证就行,免教育邮箱)
GitHub教育包的福利: 大名鼎鼎的JetBrains给学生教师的免费个人许可 https://education.github.com/pack/redeem/jetbrains 有Github学 ...
- Idea用maven给springboot打jar包
一.准备工作 1.工具:Idea2018,maven3.5 2.首先得保证pom有maven插件 <plugin> <groupId>org.springframework.b ...
- vue组件、自定义指令、路由
1.vue组件 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的 ...
- Python课程第六天作业
1.以自己的理解总结为什么会出现循环导入,并用代码举例说明 循环导入报错并不是应为相互之间引用而导致报错,实际上是在导入一个模块时发现引用的模块不存在 示例如下: m1.py from m2 impo ...
- WebStrom安装Markdown插件
安装步骤 File→Settings→Plugins→关键字搜索markdown→选择Markdown Navigator→点击Install→出现下载弹窗,等待下载完毕→重启Webstrom 效果预 ...
- Redis(九)高可用专栏之《简介篇》
在互联网的大趋势下,用户体验.服务的可用性日趋重要.任何一个服务的不可用,都可能导致连锁式功能故障. 前言 高可用模型的已经逐渐形成一种套路: 主备/主从模式 集群模式 主备/主从模式 至少有两台服务 ...
- 深入V8引擎-默认Platform之mac篇(1)
又到了常规的堆砌代码凑文章字数环节,很多API我就直接贴官方的英文释义,个人翻译其实有时候并不是很准确,搞错了甚至会误导,还是尽量自己去理解. 首先看看入口方法. std::unique_ptr< ...