题目链接: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 }