---
# System prepended metadata

title: '[Codeforces 825F String Compression](https://codeforces.com/contest/825/problem/F)'
tags: [' tutorial', codeforces]

---

---

tags: codeforces, tutorial

---

# [Codeforces 825F String Compression](https://codeforces.com/contest/825/problem/F)

KMP求最小循環節 + DP

## Solution
假設string = $a_1a_2a_3...a_{n}$

定義 $f(i)$ 表示前i個字元壓縮後的最小長度
$i \in [0, n]$, base case $f(0) = 0$

$cir{i, j}$表示$a_ja_{j+1}...a_i$ substring的最小循環節長。
$slen( x )$ 表示$x$值的十進位長度
由此我們可以寫出轉移式

$f(i) = min\{ f(i - j) + cir(i-j, i) + slen(cir(i-j, i)) \}$

### $cir$ part

對於一個string求循環節長
我們可以利用kmp algorithm的prefix function

:::info
這邊的prefix function
使用這個定義
![](https://i.imgur.com/nfugleS.png)
:::


位置$i$,  $i \in [0, n)$
如果 `(i+1) % ((i+1) - prefix[i]) == 0`
則substring $a_1a_2a_3...a_{i+1}$ 的最小循環字串長度即為`(i+1) - prefix[i]`

## sol

如果我們寫成最直覺的recurrence
```
for i = 0 to n-1 // O(n)
	for j = 0 to i // O(n)
		求substr(i-j, i)的 prefix function // O(n)
		f(i) <- min(f(i), f(i-j)+cir(i-j, i)+slen())
```

表格大小為$O(n)$
每次轉移要花$O(n\times n)$

但可以觀察到
壓縮字串的時候，如果開頭一樣，是共用同樣的prefix function
因此我們可以改寫成下列的方式
```
for i = 0 to n-1 // O(n)
	求substr(i, n-1)的 prefix function
	for j = i to n
		f(j) <- min(f(j), f(i)+cir(i, j) + slen())
```

整體時間複雜度為$O(n^2)$


## Code
```cpp
#include <bits/stdc++.h>
using namespace std;
const int inf = 2000000000;

int prefix[8000] = {0};
int dp[8001];
void gen_prefix(char* str, int len){
  prefix[0] = 0;
  for(int i = 1; i < len; i++){
    int len = prefix[i-1];
    while(len > 0 && str[len] != str[i]){
      len = prefix[len-1];
    }
    if(str[len] == str[i])
      len++;
    prefix[i] = len;
  }
}
int tostrlen(int val){
  if(val == 0)
    return 1;
  int ans = 0;
  while(val > 0)
    val /= 10, ans++;
  return ans;
}
int main(){
  char str[8000];
  scanf("%s", str);
  int len = strlen(str);
  fill(dp, dp+len+1, inf);
  dp[0] = 0;
  for (int i = 0; i < len; i++){
    gen_prefix(str + i, len - i);
    for (int j = i + 1; j <= len; j++){
      int curlen = j - i;
      int circulen = curlen - prefix[curlen-1];
      int circutime = curlen / circulen;
      if(curlen % circulen == 0 && circutime > 1){
        dp[j] = min(dp[j], dp[i] + circulen + tostrlen(circutime));
      }
      else{
        dp[j] = min(dp[j], dp[i] + curlen + 1);
      }
    }
  }
  cout << dp[len] << endl;

  return 0;
}
```



