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掉


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM