HDU 6583 Typewriter 题解

——本题来自杭电多校第一场

 

题意:给定一个字符串,主角需要用打字机将字符串打出来,每次可以:

1.花费p来打出任意一个字符

2.花费q来将已经打出的某一段(子串)复制到后面去

 

对于这种最优化的问题,我们可以考虑dp

设置dp[i]表示已经打出前i个字符的最小花费,这样设状态是没有后效性的。

那么显然有:

            dp[i] = dp[i-1] + p

这样就可以将第一种方案的转移算出来了

对于第二种方案,我们可以考虑维护一个j,使得 s[j+1……i] 是 s[1……j] 的子集(1),也就是说 s[j+1……i] 可以由 s[1……j] 中的一部分复制而来

具体实现利用后缀自动机来维护 s[1……j] 这个字符串,当不满足上述条件(1)时,就不断往后添加字符,并让 j = j + 1

当满足上述条件(1)时,就可以有:

            dp[i] = min(dp[i], dp[j] + q) 

具体细节:当找到满足条件的j时,记录在后缀自动机上最后的匹配位置,每次i或者j变化的时候,检查cur的link指针指向位置的终点集合长度是否大于等于已匹配长度,如果是,就可以往link指针方向跳(对于这一部分不理解的话建议复习SAM的终点集合的性质),之后继续匹配。

 

代码:

 

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn=200005;
 6 const int kind=26;
 7 struct state
 8 {
 9     state *Next[kind],*link;
10     int len;
11     state()
12     {
13         link=0;
14         len=0;
15         memset(Next,0,sizeof(Next));
16     }
17 };
18 int sz;
19 state st[maxn*2+5];
20 inline state* newnode(int len = 0)
21 {
22     memset(st[sz].Next,0,sizeof(st[sz].Next));
23     st[sz].link=0;
24     st[sz].len=len;
25     return &st[sz++];
26 }
27 state *root,*last;
28 void extend(int w)
29 {
30     state* p=last;
31     state* cur=newnode(p->len+1);
32     while(p&&p->Next[w]==0)
33     {
34         p->Next[w]=cur;
35         p=p->link;
36     }
37     if(p)
38     {
39         state* q=p->Next[w];
40         if(p->len+1==q->len)
41             cur->link=q;
42         else
43         {
44             state* clone=newnode(p->len+1);
45             memcpy(clone->Next,q->Next,sizeof(q->Next));
46             clone->link=q->link;
47             q->link=clone;
48             cur->link=clone;
49             while(p&&p->Next[w]==q)
50             {
51                 p->Next[w]=clone;
52                 p=p->link;
53             }
54         }
55     }
56     else cur->link=root;
57     last=cur;
58 }
59 
60 #define ll long long
61 char s[maxn];
62 ll dp[maxn];
63 int main()
64 {
65     while(~scanf("%s",s+1))
66     {
67         sz=0;
68         root=newnode();
69         last=root;
70         ll p,q;
71         scanf("%lld%lld",&p,&q);
72         int n=strlen(s+1);
73         int j=1;
74         dp[1]=p;
75         extend(s[1]-'a');
76         state* cur=root->Next[s[1]-'a'];
77         for(int i=2;i<=n;i++)
78         {
79             dp[i]=dp[i-1]+p;
80             while(1)
81             {
82                 while(cur!=root && cur->link->len>=i-j-1) cur=cur->link;
83                 if(cur->Next[s[i]-'a']!=NULL)
84                 {
85                     cur=cur->Next[s[i]-'a'];
86                     break;
87                 }
88                 else
89                     extend(s[++j]-'a');
90             }
91             //cout<<i<<' '<<j<<endl;
92             dp[i]=min(dp[i],dp[j]+q);
93         }
94         printf("%lld\n",dp[n]);
95     }
96 }
View Code

 

注:由于本代码的sam用的是指针,HDU中G++编译器的指针为8字节,所以交G++的时候可能会因为常数太大而T,但是交C++可以600ms左右A掉

posted @ 2019-07-23 15:38  Amori  阅读(812)  评论(0编辑  收藏  举报