BZOJ 1090 字符串折叠(Hash + DP)
题目链接 字符串折叠
区间DP。$f[l][r]$为字符串在区间l到r的最小值
正常情况下 $f[l][r] = min(f[l][r], f[l][l+k-1]+f[l+k][r]);$
当$l$到$r$以$k$为周期时 $f[l][r] = min(f[l][r], 2+sz(k)+f[l][l+(r-l+1)/k-1]);$
判重的时候为了方便我用了哈希……当然其他方法应该也是可以的~
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef unsigned long long LL; const int N = 210; const int base = 233; int f[N][N], n; LL bin[N], Hash[N]; char s[N]; void Hashtable(){ bin[0] = 1; rep(i, 1, n) bin[i] = bin[i - 1] * base; rep(i, 1, n) Hash[i] = Hash[i - 1] * base + s[i]; } inline LL gethash(int l, int r){ return Hash[r] - Hash[l - 1] * bin[r - l + 1]; } inline int sz(int x){ int ret = 0; for (; x;) ++ret, x /= 10; return ret; } inline bool judge(int l, int m, int r){ if ((r - l + 1) % m) return 0; int t1 = (r - l + 1) / m; LL t = gethash(l, l + t1 - 1); for (int i = l; i <= r; i += t1) if (gethash(i, i + t1 - 1) != t) return 0; return 1; } int main(){ scanf("%s", s + 1); n = strlen(s + 1); Hashtable(); rep(i, 1, n){ rep(j, 1, n){ if (j - i + 1 <= n){ int l = j, r = i + j - 1; f[l][r] = r - l + 1; rep(k, 1, i){ f[l][r] = min(f[l][r], f[l][l + k - 1] + f[l + k][r]); if (judge(l, k, r)) f[l][r] = min(f[l][r], 2 + sz(k) + f[l][l + (r - l + 1) / k - 1]); } } } } printf("%d\n", f[1][n]); return 0; }