传送门:点击打开链接
题意:可以把字符串用过括号折叠起来。现在给你原串,问折叠后最短是多少,len<=500
思路,首先,我们通过O(n^2)来预处理出每个区间里的最短循环节。
之后,我们直接按照区间dp用记忆化搜索去搞就行了。
#include <map> #include <set> #include <cmath> #include <ctime> #include <stack> #include <queue> #include <cstdio> #include <cctype> #include <bitset> #include <string> #include <vector> #include <cstring> #include <iostream> #include <algorithm> #include <functional> #define fuck(x) cout << "[" << x << "]" #define FIN freopen("input.txt", "r", stdin) #define FOUT freopen("output.txt", "w+", stdout) using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MX = 1e3 + 5; const int INF = 0x3f3f3f3f; char S[MX]; int A[MX][MX], dp[MX][MX], Next[MX]; void GetNext(int n, char S[]) { Next[0] = 0; for(int i = 1; i < n; i++) { int j = Next[i - 1]; while(j && S[i] != S[j]) j = Next[j - 1]; Next[i] = S[i] == S[j] ? j + 1 : 0; } } int GetCir(int p) { return (p + 1) % (p - Next[p] + 1) ? p + 1 : p - Next[p] + 1; } int getlen(int n) { int ret = 0; while(n) { ret++; n /= 10; } return max(ret, 1); } void solve(int l, char S[], int dp[]) { int n = strlen(S); GetNext(n, S); for(int i = 0; i < n; i++) { A[l][l + i] = GetCir(i); } } int DP(int l, int r) { if(r - l + 1 == 1) return 1; if(r - l + 1 == 2) return 2; if(dp[l][r]) return dp[l][r]; int cnt = (r - l + 1) / A[l][r], w = A[l][r], ans = r - l + 1; if(cnt != 1) ans = min(ans, DP(l, l + w - 1) + 2 + getlen(cnt)); if(A[l][r] == 1) ans = min(ans, 1 + getlen(r - l + 1)); for(int i = l; i <= r - 1; i++) { ans = min(ans, DP(l, i) + DP(i + 1, r)); } return dp[l][r] = ans; } int main() { int ansk = 0; //FIN; while(~scanf("%s", S), S[0] != '0') { memset(dp, 0, sizeof(dp)); int n = strlen(S); for(int i = 0; i < n; i++) { solve(i, S + i, A[i]); } printf("Case %d: %d\n", ++ansk, DP(0, n - 1)); } return 0; }