题意:

有个打字机,在当前字符串后新加一个字花费p,把当前字符串的一个连续子串拷贝到当前字符串的末尾花费q,给定一个字符串,求用打字机打出这个字符串的最小花费。

题解:

容易想到用dp

记dp[i]为打出前i个字符的最小花费,对于每个i,令

A=dp[i-1]+p

B=dp[j]+q 其中j为最小的,使得s[j+1~i]是s[1~j]的连续子串的值

其实就是,把这个字符串咔嚓切一刀,看后面的部分是不是前面部分的子串,如果不是,就把切的地方向后挪,前面的部分是原串,后面的部分是模式串。

dp[i]=min(A,B)

假如在i=k的时候已经找到了这么一个最优切分点j,那么在讨论i=k+1的时候,这个最优切分点要么就是原来的j,要么就需要把这个j向后挪。

为啥,因为j已经是令原串最短的切分点了,现在你还要往模式串后面加东西,原串不可能更短,只能更长。

注意,在j向后移动的过程中,出现了将模式串的第一位砍掉的操作,那么问题来了,有没有一种数据结构,支持这种将模式串的前面砍掉的操作呢?

后缀自动机。后缀自动机上一个节点代表多个连续子串,其中一个最长,剩下的都是这个最长的连续子串的后缀,父节点又是子节点的后缀,那么,对于某个自动机上的连续子串,把它最前面一位砍掉,要么它还停留在原来的节点上,要么它转移到了父节点上。

后缀自动机增量构造复杂度O(1),状态转移复杂度O(1)

总时间复杂度O(n)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn=;
#define kind=;
struct state
{
state *Next[kind],*link;
int len;
state()
{
link=;
len=;
memset(Next,,sizeof(Next));
}
};
int sz;
state st[maxn*+];
inline state* newnode(int len = )
{
memset(st[sz].Next,,sizeof(st[sz].Next));
st[sz].link=;
st[sz].len=len;
return &st[sz++];
}
state *root,*last;
void extend(int w)
{
state* p=last;
state* cur=newnode(p->len+);
while(p&&p->Next[w]==)
{
p->Next[w]=cur;
p=p->link;
}
if(p)
{
state* q=p->Next[w];
if(p->len+==q->len)
cur->link=q;
else
{
state* clone=newnode(p->len+);
memcpy(clone->Next,q->Next,sizeof(q->Next));
clone->link=q->link;
q->link=clone;
cur->link=clone;
while(p&&p->Next[w]==q)
{
p->Next[w]=clone;
p=p->link;
}
}
}
else cur->link=root;
last=cur;
} #define ll long long
char s[maxn];
ll dp[maxn];
int main()
{
while(~scanf("%s",s+))
{
sz=;
root=newnode();
last=root;
ll p,q;
scanf("%lld%lld",&p,&q);
int n=strlen(s+);
int j=;
dp[]=p;
extend(s[]-'a');
state* cur=root->Next[s[]-'a'];
for(int i=;i<=n;i++)
{
dp[i]=dp[i-]+p;
while()
{
while(cur!=root && cur->link->len>=i-j-) cur=cur->link;
if(cur->Next[s[i]-'a']!=NULL)
{
cur=cur->Next[s[i]-'a'];
break;
}
else
extend(s[++j]-'a');
}
//cout<<i<<' '<<j<<endl;
dp[i]=min(dp[i],dp[j]+q);
}
printf("%lld\n",dp[n]);
}
}

PS:本人从来没搞明白subset,substring,section,子串,子序列,区间的区别,因此一般习惯称其为“连续子串/序列”,“非连续子串/子序列”

最新文章

  1. 如何编写自己的Arduino库?
  2. C#异步下载文件--基于http请求
  3. PHP判断访问者手机移动端还是PC端的函数,亲测好用
  4. Mango DS Traning #49 ---线段树3 解题手记
  5. $key 的用法
  6. linux根分区扩容
  7. 弹性ScrollView,和下啦刷新的效果类似 实现下拉弹回和上拉弹回
  8. Codeforces Round #328 (Div. 2) D. Super M
  9. [转]33 useful Keyboard Shortcuts for Run commond
  10. C# 该行已经属于还有一个表 的解决方法
  11. Centos清理内存 内存回收释放及内存使用查看的相关命令
  12. PHP 生成.csv 文件并下载到浏览器
  13. Knockout js 绑定 radio 时,当绑定整形的时候,绑定不生效
  14. impala集成sentry
  15. [DeeplearningAI笔记]改善深层神经网络_深度学习的实用层面1.9_归一化normalization
  16. 如何阅读jdk源码?
  17. openstack Q版部署-----安装报错问题
  18. 使用ServiceStack改造我们的项目
  19. hdu 4431 第37届ACM/ICPC 天津赛区现场赛A题 枚举
  20. ios 工作日志

热门文章

  1. 【leetcode】973. K Closest Points to Origin
  2. INNODB存储引擎之缓冲池
  3. 【SQL】Mysql常用sql语句记录
  4. word文档操作
  5. docker安装(4)
  6. Machine Learning 之二,什么监督性学习,非监督性学习。
  7. 一:unittest框架配合selenium工具之CSS_selector定位。
  8. JAVA学习之Java语音基础组成
  9. centos安装vbox addition
  10. ThreadLocal浅析