[2019杭电多校第一场][hdu6583]Typewriter(后缀自动机&dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6583

大致题意是说可以花费p在字符串后添加一个任意字符,或者花费q在字符串后添加一个当前字符串的子串。问最少花费多少可以得到目标串。

一开始想到的dp,dp[i]为得到目标串的1-i的最小花费。

那么dp[i]=min{dp[i-1]+p,dp[j-1]+q},s[j~i]应该为s[1~j-1]的子串。

我们可以知道dp数组是一个单调递增的数组,用反证法可以证明:dp[i]由dp[i-1]转移就不用说了,如果dp[i]是由dp[j]转移过来且dp[i-1]>dp[i],那么dp[i-1]也可以由dp[j]转移过来,所以不合法。

因为dp数组是递增的,则dp[i]如果是由第二种方法转移过来,则j应该是最小的。所以求最小的j满足s[j~i]应该为s[1~j-1]的子串。

dp[i]的第二种转移位置,应该在是dp[i-1]的第二种转移位置j的基础上往后移。

所以用双指针的位置,一个代表i,一个代表j,每次在后缀自动机上添加第j个位置的字符,如果s[j~i]在后缀自动机上可以匹配,则转移,不然就把j往后移。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<string>
 7 #include<queue>
 8 using namespace std;
 9 typedef long long ll;
10 const ll maxn = 2e5 + 10;
11 const ll inf = 1e18;
12 struct SAM {
13     int cnt, last, now;
14     int len[maxn * 2], ch[maxn * 2][30], fa[maxn * 2];
15     void clear() {
16         for (int i = 0; i <= cnt; i++) {
17             len[i] = fa[i] = 0;
18             memset(ch[i], 0, sizeof(ch[i]));
19         }
20     }
21     void insert(int x) {
22         int np = ++cnt, p = last;
23         len[np] = len[p] + 1, last = np;
24         while (p && !ch[p][x])
25             ch[p][x] = np, p = fa[p];
26         if (!p)
27             fa[np] = 1;
28         else {
29             int q = ch[p][x];
30             if (len[q] == len[p] + 1)
31                 fa[np] = q;
32             else {
33                 int nq = ++cnt;
34                 len[nq] = len[p] + 1;
35                 memcpy(ch[nq], ch[q], sizeof(ch[nq]));
36                 fa[nq] = fa[q];
37                 fa[np] = fa[q] = nq;
38                 while (p&&ch[p][x] == q)
39                     ch[p][x] = nq, p = fa[p];
40             }
41         }
42     }
43     int Match(int x) {
44         return ch[now][x];
45     }
46     void withdraw(int lens) {
47         while (now&&len[fa[now]] >= lens)now = fa[now];
48         if (now == 0)now = 1;
49     }
50     void Tran(int x, int lens) {
51         now = ch[now][x];
52         if (now == 0)now = 1;
53         withdraw(lens);
54     }
55 }SA;
56 ll dp[maxn];
57 char s[maxn];
58 int main() {
59     while (~scanf("%s", s + 1)) {
60         ll p, q, len;
61         memset(dp, 0, sizeof(dp));
62         scanf("%lld%lld", &p, &q);
63         len = strlen(s + 1);
64         SA.cnt = SA.last = SA.now = 1;
65         SA.insert(s[1] - 'a');
66         dp[1] = p;
67         int l = 2, r = 1;
68         for (int i = 2; i <= len; i++) {
69             r++;
70             dp[i] = dp[i - 1] + p;
71             while ((!SA.Match(s[i] - 'a') || r - l + 1 > (i + 1) / 2) && l <= r) {
72                 SA.insert(s[l++] - 'a');
73                 SA.withdraw(r - l);
74             }
75             SA.Tran(s[i] - 'a', r - l + 1);
76             if (l <= r)
77                 dp[r] = min(dp[r], dp[l - 1] + q);
78         }
79         printf("%lld\n", dp[len]);
80         SA.clear();
81     }
82 
83 }
View Code

猜你喜欢

转载自www.cnblogs.com/sainsist/p/11307838.html