找循环节时可以用 KMP 优化一下,但是 100 的数据嘛当然是选择暴力出奇迹呀。
Update:
2018/03/17 做另外一道类似的题时又想到了这道题,我的代码里每次 dfs(L, R) 都做了一次 KMP,非常笨。
可以预处理一下(求出 [L, R] 的 fail 数组后, [L, L+1], [L, L+2], ... , [L, R] 这些区间的循环节都可以得到)。
只需要求 [1, N], [2, N], [3, N], ..., [N, N] 的 fail 数组就行了,这个预处理过程是 O(N^2) 的。
总结一下吧。最近做到了这样一类 DP 题,它们的方程在转移时候用到的东西需要预处理。而预处理过程
往往涉及一些算法——
可能是 DP (IOI 2000 邮局问题,NKOJ1181),可能是前缀和,可能是 KMP 。
1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 5 int len; 6 int Mem[120][120], fail[120]; 7 char S[120]; 8 9 inline int Bitcnt(int v) 10 { 11 int cnt = 0; 12 do ++cnt; 13 while (v /= 10); 14 return cnt; 15 } 16 17 int dfs(int L, int R) 18 { 19 if (Mem[L][R]) return Mem[L][R]; 20 if (L == R) return Mem[L][R] = 1; 21 22 int i, j; 23 Mem[L][R] = R - L + 1; 24 25 //KMP 26 char *tmp_S = S + L - 1; 27 int tmp_len = R - L + 1; 28 fail[1] = j = 0; 29 for (i = 2; i <= tmp_len; ++i) { 30 while (j && tmp_S[j + 1] != tmp_S[i]) 31 j = fail[j]; 32 if (tmp_S[j + 1] == tmp_S[i]) ++j; 33 fail[i] = j; 34 } 35 int tmp_cut = tmp_len - fail[tmp_len]; 36 if (tmp_len != tmp_cut && !(tmp_len % tmp_cut)) 37 Mem[L][R] = std::min(Mem[L][R], Bitcnt(tmp_len / tmp_cut) + 2 + dfs(L, L + tmp_cut - 1)); 38 39 for (i = L; i < R; ++i) 40 Mem[L][R] = std::min(Mem[L][R], dfs(L, i) + dfs(i+1, R)); 41 42 return Mem[L][R]; 43 } 44 45 int main() 46 { 47 scanf("%s", S + 1); 48 len = strlen(S + 1); 49 printf("%d ", dfs(1, len)); 50 return 0; 51 }
NKOJ2388
字符串折叠 | |
|
问题描述
折叠的定义如下:
1. 一个字符串可以看成它自身的折叠。记作S =S
2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) = SSSS…S(X个S)。
3. 如果A = A’, B=B’,则AB = A’B’
例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B) = AAACBB,而2(3(A)C)2(B)=AAACAAACBB给一个字符串,求它的最短折叠。
例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。
输入格式
仅一行,即字符串S,长度保证不超过100。
输出格式
仅一行,即最短的折叠长度
样例输入
NEERCYESYESYESNEERCYESYESYES
样例输出
14